diff --git a/Assets/DefaultPrefabObjects.asset b/Assets/DefaultPrefabObjects.asset new file mode 100644 index 0000000..27e5f59 --- /dev/null +++ b/Assets/DefaultPrefabObjects.asset @@ -0,0 +1,22 @@ +%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: 4512293259955182956, guid: 300370bdf7819da41937e0beac65b32c, type: 3} + - {fileID: 611616139817875448, guid: bf5f023b4017a5e41a9815ec5745df3d, type: 3} + - {fileID: 9117857247562382210, guid: b2991431a5f893e49937d01b6da44ff8, type: 3} + - {fileID: 3624261834906416601, guid: e3f599c0ac723194a92f51c91ec73267, type: 3} + - {fileID: 3624261834906416601, guid: 4e1673ccc2acac543871855a0e8bed71, type: 3} + - {fileID: 201277550, guid: d904f93bc171bb144ba33c5155282f6f, type: 3} + - {fileID: 27039729695437543, guid: f820efff6a2871447b961fc755212ba3, type: 3} diff --git a/Assets/DefaultPrefabObjects.asset.meta b/Assets/DefaultPrefabObjects.asset.meta new file mode 100644 index 0000000..56c01ef --- /dev/null +++ b/Assets/DefaultPrefabObjects.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e6b71f999cc8fc647a3f73f7088802e0 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet.meta b/Assets/FishNet.meta new file mode 100644 index 0000000..970e7b9 --- /dev/null +++ b/Assets/FishNet.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1c10e8242689baa4795de57f9ef42ec3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating.meta b/Assets/FishNet/CodeGenerating.meta new file mode 100644 index 0000000..0da94fd --- /dev/null +++ b/Assets/FishNet/CodeGenerating.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bbb4974b4302f435b9f4663c64d8f803 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Extension.meta b/Assets/FishNet/CodeGenerating/Extension.meta new file mode 100644 index 0000000..fd3a139 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Extension.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 206db668838ebc34b90ae36be24ce3be +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Extension/ILProcessorExtensions.cs b/Assets/FishNet/CodeGenerating/Extension/ILProcessorExtensions.cs new file mode 100644 index 0000000..7e1c18a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Extension/ILProcessorExtensions.cs @@ -0,0 +1,20 @@ +using FishNet.CodeGenerating.Helping.Extension; +using MonoFN.Cecil.Cil; + +namespace FishNet.CodeGenerating.Extension +{ + + + internal static class ILProcessorExtensions + { + /// + /// Creates a variable type within the body and returns it's VariableDef. + /// + internal static VariableDefinition CreateVariable(this ILProcessor processor, CodegenSession session, System.Type variableType) + { + return processor.Body.Method.CreateVariable(session, variableType); + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Extension/ILProcessorExtensions.cs.meta b/Assets/FishNet/CodeGenerating/Extension/ILProcessorExtensions.cs.meta new file mode 100644 index 0000000..01e9737 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Extension/ILProcessorExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 127d8312da53b3e49ba9e3e4c6348857 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Extension/MethodDefinitionExtensions.cs b/Assets/FishNet/CodeGenerating/Extension/MethodDefinitionExtensions.cs new file mode 100644 index 0000000..1d42d6d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Extension/MethodDefinitionExtensions.cs @@ -0,0 +1,131 @@ +using FishNet.CodeGenerating.Helping.Extension; +using MonoFN.Cecil; +using MonoFN.Cecil.Rocks; +using MonoFN.Collections.Generic; +using System.Collections.Generic; + +namespace FishNet.CodeGenerating.Extension +{ + + + internal static class MethodDefinitionExtensions + { + /// + /// Returns the proper OpCode to use for call methods. + /// + 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; + } + /// + /// Returns the proper OpCode to use for call methods. + /// + public static MonoFN.Cecil.Cil.OpCode GetCallOpCode(this MethodReference mr, CodegenSession session) + { + return mr.CachedResolve(session).GetCallOpCode(); + } + + /// + /// Adds otherMd parameters to thisMR and returns added parameters. + /// + public static List CreateParameters(this MethodReference thisMr, CodegenSession session, MethodDefinition otherMd) + { + return thisMr.CachedResolve(session).CreateParameters(session, otherMd); + } + /// + /// Adds otherMr parameters to thisMR and returns added parameters. + /// + public static List CreateParameters(this MethodReference thisMr, CodegenSession session, MethodReference otherMr) + { + return thisMr.CachedResolve(session).CreateParameters(session, otherMr.CachedResolve(session)); + } + + /// + /// Adds otherMd parameters to thisMd and returns added parameters. + /// + public static List CreateParameters(this MethodDefinition thisMd, CodegenSession session, MethodDefinition otherMd) + { + List results = new List(); + + foreach (ParameterDefinition pd in otherMd.Parameters) + { + session.ImportReference(pd.ParameterType); + int currentCount = thisMd.Parameters.Count; + string name = (pd.Name + currentCount); + ParameterDefinition parameterDef = new ParameterDefinition(name, pd.Attributes, pd.ParameterType); + //Set any default values. + parameterDef.Constant = pd.Constant; + parameterDef.IsReturnValue = pd.IsReturnValue; + parameterDef.IsOut = pd.IsOut; + foreach (CustomAttribute item in pd.CustomAttributes) + parameterDef.CustomAttributes.Add(item); + parameterDef.HasConstant = pd.HasConstant; + parameterDef.HasDefault = pd.HasDefault; + + thisMd.Parameters.Add(parameterDef); + + results.Add(parameterDef); + } + + return results; + } + + /// + /// Returns a method reference while considering if declaring type is generic. + /// + public static MethodReference GetMethodReference(this MethodDefinition md, CodegenSession session) + { + MethodReference methodRef = session.ImportReference(md); + + //Is generic. + if (md.DeclaringType.HasGenericParameters) + { + GenericInstanceType git = methodRef.DeclaringType.MakeGenericInstanceType(); + MethodReference result = new MethodReference(md.Name, md.ReturnType) + { + HasThis = md.HasThis, + ExplicitThis = md.ExplicitThis, + DeclaringType = git, + CallingConvention = md.CallingConvention, + }; + foreach (ParameterDefinition pd in md.Parameters) + { + session.ImportReference(pd.ParameterType); + result.Parameters.Add(pd); + } + return result; + } + else + { + return methodRef; + } + } + + + /// + /// Returns a method reference for a generic method. + /// + public static MethodReference GetMethodReference(this MethodDefinition md, CodegenSession session, TypeReference typeReference) + { + MethodReference methodRef = session.ImportReference(md); + return methodRef.GetMethodReference(session, typeReference); + } + + + /// + /// Returns a method reference for a generic method. + /// + public static MethodReference GetMethodReference(this MethodDefinition md, CodegenSession session, TypeReference[] typeReferences) + { + MethodReference methodRef = session.ImportReference(md); + return methodRef.GetMethodReference(session, typeReferences); + } + + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Extension/MethodDefinitionExtensions.cs.meta b/Assets/FishNet/CodeGenerating/Extension/MethodDefinitionExtensions.cs.meta new file mode 100644 index 0000000..784e564 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Extension/MethodDefinitionExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 866ed457fe28c3e4b9698d87b9abd709 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Extension/TypeDefinitionExtensions.cs b/Assets/FishNet/CodeGenerating/Extension/TypeDefinitionExtensions.cs new file mode 100644 index 0000000..6c11c26 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Extension/TypeDefinitionExtensions.cs @@ -0,0 +1,254 @@ +using FishNet.CodeGenerating.Helping.Extension; +using MonoFN.Cecil; + +namespace FishNet.CodeGenerating.Extension +{ + + + internal static class TypeDefinitionExtensions + { + + + /// + /// Returns if a TypeDefinition is nullable. + /// + public static bool IsNullable(this TypeDefinition td) + { + return (td.Name == typeof(System.Nullable<>).Name); + } + + + public static MethodReference GetMethodReferenceInBase(this TypeDefinition td, CodegenSession session, string methodName) + { + MethodDefinition baseMd = td.GetMethodDefinitionInBase(session, methodName); + if (baseMd == null) + return null; + + + MethodReference baseMr; + TypeReference baseTr = td.BaseType; + if (baseTr.CachedResolve(session).HasGenericParameters) + { + GenericInstanceType git = (GenericInstanceType)baseTr; + baseMr = new MethodReference(baseMd.Name, baseMd.ReturnType, git) + { + HasThis = baseMd.HasThis, + CallingConvention = baseMd.CallingConvention, + ExplicitThis = baseMd.ExplicitThis, + }; + foreach (ParameterDefinition pd in baseMd.Parameters) + { + session.ImportReference(pd.ParameterType); + baseMr.Parameters.Add(pd); + } + } + else + { + baseMr = session.ImportReference(baseMd); + } + + return baseMr; + } + /// + /// Returns a method in the next base class. + /// + 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); + } + + + /// + /// Returns a method in the next base class. + /// + 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); + } + + /// + /// Gets a MethodReference or creates one if missing. + /// + public static MethodReference GetOrCreateMethodReference(this TypeDefinition td, CodegenSession session, string methodName, MethodAttributes attributes, TypeReference returnType, out bool created) + { + MethodDefinition md = td.GetMethod(methodName); + //Not found. + if (md == null) + { + md = new MethodDefinition(methodName, attributes, returnType); + td.Methods.Add(md); + created = true; + } + else + { + created = false; + } + + return md.GetMethodReference(session); + } + + + /// + /// Gets a MethodDefinition or creates one if missing. + /// + public static MethodDefinition GetOrCreateMethodDefinition(this TypeDefinition td, CodegenSession session, string methodName, MethodAttributes attributes, TypeReference returnType, out bool created) + { + MethodDefinition md = td.GetMethod(methodName); + //Not found. + if (md == null) + { + md = new MethodDefinition(methodName, attributes, returnType); + td.Methods.Add(md); + created = true; + } + else + { + created = false; + } + + return md; + } + + /// + /// Gets a MethodDefinition or creates one if missing. + /// + public static MethodDefinition GetOrCreateMethodDefinition(this TypeDefinition td, CodegenSession session, string methodName, MethodDefinition methodTemplate, bool copyParameters, out bool created) + { + MethodDefinition md = td.GetMethod(methodName); + //Not found. + if (md == null) + { + TypeReference returnType = session.ImportReference(methodTemplate.ReturnType); + md = new MethodDefinition(methodName, methodTemplate.Attributes, returnType) + { + ExplicitThis = methodTemplate.ExplicitThis, + AggressiveInlining = methodTemplate.AggressiveInlining, + Attributes = methodTemplate.Attributes, + CallingConvention = methodTemplate.CallingConvention, + HasThis = methodTemplate.HasThis, + }; + md.Body.InitLocals = methodTemplate.Body.InitLocals; + + if (copyParameters) + { + foreach (ParameterDefinition pd in methodTemplate.Parameters) + md.Parameters.Add(pd); + } + + td.Methods.Add(md); + created = true; + } + else + { + created = false; + } + + return md; + } + + + + /// + /// Returns a method in any inherited classes. The first found method is returned. + /// + 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; + } + + /// + /// Returns the next base type. + /// + public static TypeDefinition GetNextBaseTypeDefinition(this TypeDefinition typeDef, CodegenSession session) + { + return (typeDef.BaseType == null) ? null : typeDef.BaseType.CachedResolve(session); + } + + /// + /// Creates a FieldReference. + /// + public static FieldReference CreateFieldReference(this FieldDefinition fd, CodegenSession session) + { + FieldReference fr; + TypeDefinition declaringType = fd.DeclaringType; + //Is generic. + if (declaringType.HasGenericParameters) + { + GenericInstanceType git = new GenericInstanceType(declaringType); + foreach (GenericParameter item in declaringType.GenericParameters) + git.GenericArguments.Add(item); + fr = new FieldReference(fd.Name, fd.FieldType, git); + return fr; + } + //Not generic. + else + { + return session.ImportReference(fd); + } + } + + /// + /// Gets a FieldReference or creates it if missing. + /// + 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; + } + + /// + /// Creates a FieldReference. + /// + public static FieldReference CreateFieldDefinition(this TypeDefinition td, CodegenSession session, string fieldName, FieldAttributes attributes, TypeReference fieldTypeRef) + { + FieldDefinition fd = new FieldDefinition(fieldName, attributes, fieldTypeRef); + td.Fields.Add(fd); + return fd.CreateFieldReference(session); + } + + + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Extension/TypeDefinitionExtensions.cs.meta b/Assets/FishNet/CodeGenerating/Extension/TypeDefinitionExtensions.cs.meta new file mode 100644 index 0000000..dfb8256 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Extension/TypeDefinitionExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c9f00cf3dc8b90b469c3c9cb8b87fc88 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Extension/TypeReferenceExtensions.cs b/Assets/FishNet/CodeGenerating/Extension/TypeReferenceExtensions.cs new file mode 100644 index 0000000..35ab37f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Extension/TypeReferenceExtensions.cs @@ -0,0 +1,59 @@ + +using FishNet.CodeGenerating.Helping; +using FishNet.CodeGenerating.Helping.Extension; +using MonoFN.Cecil; +using UnityEngine; + +namespace FishNet.CodeGenerating.Extension +{ + + + internal static class TypeReferenceExtensions + { + + /// + /// Returns if a TypeReference is nullable. + /// + public static bool IsNullable(this TypeReference tr, CodegenSession session) + { + TypeDefinition td = tr.CachedResolve(session); + return td.IsNullable(); + } + + /// + /// Returns the fullname of a TypeReference without <>. + /// + /// + /// + public static string GetFullnameWithoutBrackets(this TypeReference tr) + { + string str = tr.FullName; + str = str.Replace("<", ""); + str = str.Replace(">", ""); + return str; + } + + /// + /// Returns a method in the next base class. + /// + public static MethodReference GetMethodInBase(this TypeReference tr, CodegenSession session, string methodName) + { + return tr.CachedResolve(session).GetMethodInBase(session, methodName); + } + + /// + /// Makes a GenericInstanceType. + /// + public static GenericInstanceType MakeGenericInstanceType(this TypeReference self) + { + GenericInstanceType instance = new GenericInstanceType(self); + foreach (GenericParameter argument in self.GenericParameters) + instance.GenericArguments.Add(argument); + + return instance; + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Extension/TypeReferenceExtensions.cs.meta b/Assets/FishNet/CodeGenerating/Extension/TypeReferenceExtensions.cs.meta new file mode 100644 index 0000000..19d7526 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Extension/TypeReferenceExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 239b1b10db80c594d93b7ba4ee2c1ec5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/FN_README.txt b/Assets/FishNet/CodeGenerating/FN_README.txt new file mode 100644 index 0000000..3efe3cf --- /dev/null +++ b/Assets/FishNet/CodeGenerating/FN_README.txt @@ -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. diff --git a/Assets/FishNet/CodeGenerating/FN_README.txt.meta b/Assets/FishNet/CodeGenerating/FN_README.txt.meta new file mode 100644 index 0000000..04e8428 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/FN_README.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9133eb285bd7f3c4f89f4d7a2a079c6b +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers.meta b/Assets/FishNet/CodeGenerating/Helpers.meta new file mode 100644 index 0000000..0dc1ce9 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: efeca2428bd9fe64d872a626b93ff0cf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/AttributeHelper.cs b/Assets/FishNet/CodeGenerating/Helpers/AttributeHelper.cs new file mode 100644 index 0000000..15b6c4f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/AttributeHelper.cs @@ -0,0 +1,94 @@ +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.Object; +using FishNet.Object.Helping; +using FishNet.Object.Prediction; +using FishNet.Object.Synchronizing; +using MonoFN.Cecil; + + +namespace FishNet.CodeGenerating.Helping +{ + internal class AttributeHelper : CodegenBase + { + #region Reflection references. + internal string ReplicateAttribute_FullName; + internal string ReconcileAttribute_FullName; + private string ServerAttribute_FullName; + private string ClientAttribute_FullName; + private string ServerRpcAttribute_FullName; + private string ObserversRpcAttribute_FullName; + private string TargetRpcAttribute_FullName; + private string SyncVarAttribute_FullName; + private string SyncObjectAttribute_FullName; + #endregion + + public override bool ImportReferences() + { + ServerAttribute_FullName = typeof(ServerAttribute).FullName; + ClientAttribute_FullName = typeof(ClientAttribute).FullName; + ServerRpcAttribute_FullName = typeof(ServerRpcAttribute).FullName; + ObserversRpcAttribute_FullName = typeof(ObserversRpcAttribute).FullName; + TargetRpcAttribute_FullName = typeof(TargetRpcAttribute).FullName; + SyncVarAttribute_FullName = typeof(SyncVarAttribute).FullName; + SyncObjectAttribute_FullName = typeof(SyncObjectAttribute).FullName; + ReplicateAttribute_FullName = typeof(ReplicateAttribute).FullName; + ReconcileAttribute_FullName = typeof(ReconcileAttribute).FullName; + + return true; + } + + /// + /// Returns type of Rpc attributeFullName is for. + /// + /// + /// + 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; + } + + + /// + /// Returns type of Rpc attributeFullName is for. + /// + /// + /// + internal QolAttributeType GetQolAttributeType(string attributeFullName) + { + if (attributeFullName == ServerAttribute_FullName) + return QolAttributeType.Server; + else if (attributeFullName == ClientAttribute_FullName) + return QolAttributeType.Client; + else + return QolAttributeType.None; + } + + + /// + /// Returns if attribute if a SyncVarAttribute. + /// + /// + /// + public bool IsSyncVarAttribute(string attributeFullName) + { + return (attributeFullName == SyncVarAttribute_FullName); + } + /// + /// Returns if attribute if a SyncObjectAttribute. + /// + /// + /// + public bool IsSyncObjectAttribute(string attributeFullName) + { + return (attributeFullName == SyncObjectAttribute_FullName); + } + } + +} diff --git a/Assets/FishNet/CodeGenerating/Helpers/AttributeHelper.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/AttributeHelper.cs.meta new file mode 100644 index 0000000..d84b8e4 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/AttributeHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d32f3dc23b55598429c5cfe6156e6243 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/CodegenSession.cs b/Assets/FishNet/CodeGenerating/Helpers/CodegenSession.cs new file mode 100644 index 0000000..6448c24 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/CodegenSession.cs @@ -0,0 +1,194 @@ +using FishNet.CodeGenerating.Helping; +using FishNet.CodeGenerating.ILCore; +using FishNet.CodeGenerating.Processing; +using FishNet.CodeGenerating.Processing.Rpc; +using MonoFN.Cecil; +using System.Collections.Generic; +using System.Linq; +using Unity.CompilationPipeline.Common.Diagnostics; +#if !UNITY_2020_1_OR_NEWER +using UnityEngine; +#endif +using SR = System.Reflection; + + +namespace FishNet.CodeGenerating +{ + + internal class CodegenSession + { + /// + /// Current module for this session. + /// + internal ModuleDefinition Module; + /// + /// Outputs errors when codegen fails. + /// + internal List Diagnostics; + /// + /// SyncVars that are being accessed from an assembly other than the currently being processed one. + /// + internal List DifferentAssemblySyncVars = new List(); + + + /// + /// CodegenBase classes for processing a module. + /// + private List _bases; + /// + /// Quick lookup of base classes. + /// + private Dictionary _basesCache = new Dictionary(); + + /// + /// Returns class of type if found within CodegenBase classes. + /// + /// + /// + internal T GetClass() where T : CodegenBase + { + string tName = typeof(T).Name; + return (T)_basesCache[tName]; + } + /// + /// Resets all helpers while importing any information needed by them. + /// + /// + /// + internal bool Initialize(ModuleDefinition module) + { + Module = module; + Diagnostics = new List(); + + _bases = new List() + { + new ReaderImports(), new ReaderProcessor() + ,new WriterImports(), new WriterProcessor() + , new PhysicsHelper(), new TimeManagerHelper(), new AttributeHelper(), new GeneralHelper() + , new ObjectHelper(), new NetworkBehaviourHelper() + , new CreatedSyncVarGenerator(), new TransportHelper() + , new NetworkConnectionImports(), new PredictedObjectHelper(), new GeneratorHelper() + , new CustomSerializerProcessor() + , new NetworkBehaviourProcessor() + , new QolAttributeProcessor() + , new RpcProcessor() + , new NetworkBehaviourSyncProcessor() + , new PredictionProcessor() + }; + + //Add all to dictionary first, then import. + foreach (CodegenBase item in _bases) + { + string tName = item.GetType().Name; + _basesCache.Add(tName, item); + } + + //Initialize. + foreach (CodegenBase item in _bases) + { + item.Initialize(this); + if (!item.ImportReferences()) + return false; + } + + return true; + } + + + + + #region Logging. + /// + /// Logs a warning. + /// + /// + internal void LogWarning(string msg) + { +#if UNITY_2020_1_OR_NEWER + Diagnostics.AddWarning(msg); +#else + Debug.LogWarning(msg); +#endif + } + /// + /// Logs an error. + /// + /// + internal void LogError(string msg) + { +#if UNITY_2020_1_OR_NEWER + Diagnostics.AddError(msg); +#else + Debug.LogError(msg); +#endif + } + #endregion + + #region ImportReference. + + public MethodReference ImportReference(SR.MethodBase method) + { + return Module.ImportReference(method); + } + + public MethodReference ImportReference(SR.MethodBase method, IGenericParameterProvider context) + { + return Module.ImportReference(method, context); + } + + public TypeReference ImportReference(TypeReference type) + { + return Module.ImportReference(type); + } + + public TypeReference ImportReference(TypeReference type, IGenericParameterProvider context) + { + return Module.ImportReference(type, context); + } + + public FieldReference ImportReference(FieldReference field) + { + return Module.ImportReference(field); + } + + public FieldReference ImportReference(FieldReference field, IGenericParameterProvider context) + { + return Module.ImportReference(field, context); + } + public MethodReference ImportReference(MethodReference method) + { + return Module.ImportReference(method); + } + + public MethodReference ImportReference(MethodReference method, IGenericParameterProvider context) + { + return Module.ImportReference(method, context); + } + public TypeReference ImportReference(System.Type type) + { + return ImportReference(type, null); + } + + + public TypeReference ImportReference(System.Type type, IGenericParameterProvider context) + { + return Module.ImportReference(type, context); + } + + + public FieldReference ImportReference(SR.FieldInfo field) + { + return Module.ImportReference(field); + } + + public FieldReference ImportReference(SR.FieldInfo field, IGenericParameterProvider context) + { + return Module.ImportReference(field, context); + } + + #endregion + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/CodegenSession.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/CodegenSession.cs.meta new file mode 100644 index 0000000..3373edc --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/CodegenSession.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3e8416eee3308f54fa942003de975420 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/CreatedSyncVarGenerator.cs b/Assets/FishNet/CodeGenerating/Helpers/CreatedSyncVarGenerator.cs new file mode 100644 index 0000000..89b1cc7 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/CreatedSyncVarGenerator.cs @@ -0,0 +1,121 @@ +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.CodeGenerating.Processing; +using FishNet.Object.Synchronizing; +using FishNet.Object.Synchronizing.Internal; +using MonoFN.Cecil; +using MonoFN.Cecil.Rocks; +using System; +using System.Collections.Generic; + +namespace FishNet.CodeGenerating.Helping +{ + internal class CreatedSyncVarGenerator : CodegenBase + { + private readonly Dictionary _createdSyncVars = new Dictionary(); + + #region Relfection references. + private TypeReference _syncBase_TypeRef; + internal TypeReference SyncVar_TypeRef; + private MethodReference _syncVar_Constructor_MethodRef; + #endregion + + #region Const. + private const string GETVALUE_NAME = "GetValue"; + private const string SETVALUE_NAME = "SetValue"; + #endregion + + /* //feature add and test the dirty boolean changes + * eg... instead of base.Dirty() + * do if (!base.Dirty()) return false; + * See synclist for more info. */ + + /// + /// Imports references needed by this helper. + /// + /// + /// + public override bool ImportReferences() + { + SyncVar_TypeRef = base.ImportReference(typeof(SyncVar<>)); + MethodDefinition svConstructor = SyncVar_TypeRef.GetFirstConstructor(base.Session, true); + _syncVar_Constructor_MethodRef = base.ImportReference(svConstructor); + + Type syncBaseType = typeof(SyncBase); + _syncBase_TypeRef = base.ImportReference(syncBaseType); + + return true; + } + + /// + /// Gets and optionally creates data for SyncVar + /// + /// + /// + internal CreatedSyncVar GetCreatedSyncVar(FieldDefinition originalFd, bool createMissing) + { + TypeReference dataTr = originalFd.FieldType; + TypeDefinition dataTd = dataTr.CachedResolve(base.Session); + + string typeHash = dataTr.FullName + dataTr.IsArray.ToString(); + + if (_createdSyncVars.TryGetValue(typeHash, out CreatedSyncVar createdSyncVar)) + { + return createdSyncVar; + } + else + { + if (!createMissing) + return null; + + base.ImportReference(dataTd); + + GenericInstanceType syncVarGit = SyncVar_TypeRef.MakeGenericInstanceType(new TypeReference[] { dataTr }); + TypeReference genericDataTr = syncVarGit.GenericArguments[0]; + + //Make sure can serialize. + bool canSerialize = base.GetClass().HasSerializerAndDeserializer(genericDataTr, true); + if (!canSerialize) + { + base.LogError($"SyncVar {originalFd.Name} data type {genericDataTr.FullName} does not support serialization. Use a supported type or create a custom serializer."); + return null; + } + + //Set needed methods from syncbase. + MethodReference setSyncIndexMr; + MethodReference genericSyncVarCtor = _syncVar_Constructor_MethodRef.MakeHostInstanceGeneric(base.Session, syncVarGit); + + if (!base.GetClass().SetSyncBaseMethods(_syncBase_TypeRef.CachedResolve(base.Session), out setSyncIndexMr, out _)) + return null; + + MethodReference setValueMr = null; + MethodReference getValueMr = null; + foreach (MethodDefinition md in SyncVar_TypeRef.CachedResolve(base.Session).Methods) + { + //GetValue. + if (md.Name == GETVALUE_NAME) + { + MethodReference mr = base.ImportReference(md); + getValueMr = mr.MakeHostInstanceGeneric(base.Session, syncVarGit); + } + //SetValue. + else if (md.Name == SETVALUE_NAME) + { + MethodReference mr = base.ImportReference(md); + setValueMr = mr.MakeHostInstanceGeneric(base.Session, syncVarGit); + } + } + + if (setValueMr == null || getValueMr == null) + return null; + + CreatedSyncVar csv = new CreatedSyncVar(syncVarGit, dataTd, getValueMr, setValueMr, setSyncIndexMr, null, genericSyncVarCtor); + _createdSyncVars.Add(typeHash, csv); + return csv; + } + } + + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/CreatedSyncVarGenerator.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/CreatedSyncVarGenerator.cs.meta new file mode 100644 index 0000000..086dc59 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/CreatedSyncVarGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 935ec97b96b35b94f8c6880c6908304c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension.meta b/Assets/FishNet/CodeGenerating/Helpers/Extension.meta new file mode 100644 index 0000000..a4c0b09 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: df90983b61081f84b990ac494ac8bdb6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/CustomAttributeExtensions.cs b/Assets/FishNet/CodeGenerating/Helpers/Extension/CustomAttributeExtensions.cs new file mode 100644 index 0000000..b11e91c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/CustomAttributeExtensions.cs @@ -0,0 +1,54 @@ +using MonoFN.Cecil; +using System.Linq; + +namespace FishNet.CodeGenerating.Helping.Extension +{ + + internal static class CustomAttributeExtensions + { + /// + /// Finds a field within an attribute. + /// + /// + /// + /// + /// + /// + internal static T GetField(this CustomAttribute customAttr, string field, T defaultValue) + { + foreach (CustomAttributeNamedArgument customField in customAttr.Fields) + { + if (customField.Name == field) + { + return (T)customField.Argument.Value; + } + } + + return defaultValue; + } + + /// + /// Returns if any of the attributes match IAtrribute. + /// + /// + /// + /// + internal static bool HasCustomAttribute(this ICustomAttributeProvider attributeProvider) + { + return attributeProvider.CustomAttributes.Any(attr => attr.AttributeType.Is()); + } + + /// + /// Returns if ca is of type target. + /// + /// + /// + /// + internal static bool Is(this CustomAttribute ca, string targetFullName) + { + return ca.AttributeType.FullName == targetFullName; + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/CustomAttributeExtensions.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/Extension/CustomAttributeExtensions.cs.meta new file mode 100644 index 0000000..87b14e3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/CustomAttributeExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a66d771ab331fae408142a5c04abd74e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/Diagnostics.cs b/Assets/FishNet/CodeGenerating/Helpers/Extension/Diagnostics.cs new file mode 100644 index 0000000..5fda8fe --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/Diagnostics.cs @@ -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 diagnostics, string message) + { + diagnostics.AddMessage(DiagnosticType.Error, (SequencePoint)null, message); + } + + internal static void AddWarning(this List diagnostics, string message) + { + diagnostics.AddMessage(DiagnosticType.Warning, (SequencePoint)null, message); + } + + internal static void AddError(this List diagnostics, MethodDefinition methodDef, string message) + { + diagnostics.AddMessage(DiagnosticType.Error, methodDef.DebugInformation.SequencePoints.FirstOrDefault(), message); + } + + internal static void AddMessage(this List diagnostics, DiagnosticType diagnosticType, SequencePoint sequencePoint, string message) + { + diagnostics.Add(new DiagnosticMessage + { + DiagnosticType = diagnosticType, + File = sequencePoint?.Document.Url.Replace($"{Environment.CurrentDirectory}{Path.DirectorySeparatorChar}", ""), + Line = sequencePoint?.StartLine ?? 0, + Column = sequencePoint?.StartColumn ?? 0, + MessageData = $" - {message}" + }); + } + + } +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/Diagnostics.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/Extension/Diagnostics.cs.meta new file mode 100644 index 0000000..a70954f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/Diagnostics.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fb7b65b572b01444cbe3c9d830cd3587 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/FieldReferenceExtensions.cs b/Assets/FishNet/CodeGenerating/Helpers/Extension/FieldReferenceExtensions.cs new file mode 100644 index 0000000..803d4de --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/FieldReferenceExtensions.cs @@ -0,0 +1,38 @@ +using FishNet.CodeGenerating.Extension; +using MonoFN.Cecil; + +namespace FishNet.CodeGenerating.Helping.Extension +{ + + internal static class FieldReferenceExtensions + { + + /// + /// Gets a Resolve favoring cached results first. + /// + internal static FieldDefinition CachedResolve(this FieldReference fieldRef, CodegenSession session) + { + return session.GetClass().GetFieldReferenceResolve(fieldRef); + } + + + public static FieldReference MakeHostGenericIfNeeded(this FieldDefinition fd, CodegenSession session) + { + TypeReference declaringTr = fd.DeclaringType; + + if (declaringTr.HasGenericParameters) + { + GenericInstanceType git = declaringTr.MakeGenericInstanceType(); + FieldReference result = new FieldReference(fd.Name, fd.FieldType, git); + return result; + } + else + { + return session.ImportReference(fd); + } + } + + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/FieldReferenceExtensions.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/Extension/FieldReferenceExtensions.cs.meta new file mode 100644 index 0000000..c868340 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/FieldReferenceExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6d983ebd0c9e1b745902030c2f7a8e99 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/GetConstructor.cs b/Assets/FishNet/CodeGenerating/Helpers/Extension/GetConstructor.cs new file mode 100644 index 0000000..2d38cdd --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/GetConstructor.cs @@ -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 + { + + /// + /// Gets the first constructor that optionally has, or doesn't have parameters. + /// + /// + /// + public static MethodDefinition GetFirstConstructor(this TypeReference typeRef, CodegenSession session, bool requireParameters) + { + return typeRef.CachedResolve(session).GetFirstConstructor(requireParameters); + } + /// + /// Gets the first constructor that optionally has, or doesn't have parameters. + /// + /// + /// + 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; + } + + /// + /// Gets the first public constructor with no parameters. + /// + /// + public static MethodDefinition GetConstructor(this TypeReference typeRef, CodegenSession session) + { + return typeRef.CachedResolve(session).GetConstructor(); + } + /// + /// Gets the first public constructor with no parameters. + /// + /// + public static MethodDefinition GetConstructor(this TypeDefinition typeDef) + { + foreach (MethodDefinition methodDef in typeDef.Methods) + { + if (methodDef.IsConstructor && methodDef.IsPublic && methodDef.Parameters.Count == 0) + return methodDef; + } + + return null; + } + + /// + /// Gets all constructors on typeDef. + /// + /// + public static List GetConstructors(this TypeDefinition typeDef) + { + List lst = new List(); + foreach (MethodDefinition methodDef in typeDef.Methods) + { + if (methodDef.IsConstructor) + lst.Add(methodDef); + } + + return lst; + } + + + /// + /// Gets constructor which has arguments. + /// + /// + /// + public static MethodDefinition GetConstructor(this TypeReference typeRef, CodegenSession session, Type[] arguments) + { + return typeRef.CachedResolve(session).GetConstructor(arguments); + } + + /// + /// Gets constructor which has arguments. + /// + /// + /// + 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; + } + + + /// + /// Gets constructor which has arguments. + /// + /// + /// + public static MethodDefinition GetConstructor(this TypeReference typeRef, CodegenSession session, TypeReference[] arguments) + { + return typeRef.CachedResolve(session).GetConstructor(arguments); + } + + /// + /// Gets constructor which has arguments. + /// + /// + /// + 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; + } + + /// + /// Resolves the constructor with parameterCount for typeRef. + /// + /// + /// + public static MethodDefinition GetConstructor(this TypeReference typeRef, CodegenSession session, int parameterCount) + { + return typeRef.CachedResolve(session).GetConstructor(parameterCount); + } + + + /// + /// Resolves the constructor with parameterCount for typeRef. + /// + /// + /// + 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; + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/GetConstructor.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/Extension/GetConstructor.cs.meta new file mode 100644 index 0000000..c66fc8f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/GetConstructor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1a7e03137ca78704e999f3a3dc68b953 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/ILProcessorExtensions.cs b/Assets/FishNet/CodeGenerating/Helpers/Extension/ILProcessorExtensions.cs new file mode 100644 index 0000000..2ff3d20 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/ILProcessorExtensions.cs @@ -0,0 +1,169 @@ +using MonoFN.Cecil; +using MonoFN.Cecil.Cil; +using System.Collections.Generic; + +namespace FishNet.CodeGenerating.Helping.Extension +{ + + internal static class ILProcessorExtensions + { + + /// + /// Creates a debug log for text without any conditions. + /// + public static void DebugLog(this ILProcessor processor, CodegenSession session, string txt) + { + processor.Emit(OpCodes.Ldstr, txt); + processor.Emit(OpCodes.Call, session.GetClass().Debug_LogCommon_MethodRef); + } + /// + /// Creates a debug log for vd without any conditions. + /// + public static void DebugLog(this ILProcessor processor, CodegenSession session, VariableDefinition vd) + { + processor.Emit(OpCodes.Ldloc, vd); + processor.Emit(OpCodes.Box, vd.VariableType); + processor.Emit(OpCodes.Call, session.GetClass().Debug_LogCommon_MethodRef); + } + /// + /// Creates a debug log for vd without any conditions. + /// + public static void DebugLog(this ILProcessor processor, CodegenSession session, FieldDefinition fd, bool loadArg0) + { + if (loadArg0) + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, fd); + processor.Emit(OpCodes.Box, fd.FieldType); + processor.Emit(OpCodes.Call, session.GetClass().Debug_LogCommon_MethodRef); + } + /// + /// Creates a debug log for pd without any conditions. + /// + public static void DebugLog(this ILProcessor processor, CodegenSession session, ParameterDefinition pd) + { + processor.Emit(OpCodes.Ldloc, pd); + processor.Emit(OpCodes.Box, pd.ParameterType); + processor.Emit(OpCodes.Call, session.GetClass().Debug_LogCommon_MethodRef); + } + + ///// + ///// Creates a debug log for mr without any conditions. + ///// + //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().Debug_LogCommon_MethodRef); + //} + + + /// + /// Inserts instructions at the beginning. + /// + /// + /// + public static void InsertAt(this ILProcessor processor, int target, List instructions) + { + for (int i = 0; i < instructions.Count; i++) + processor.Body.Instructions.Insert(i + target, instructions[i]); + } + + + /// + /// Inserts instructions at the beginning. + /// + /// + /// + public static void InsertFirst(this ILProcessor processor, List instructions) + { + for (int i = 0; i < instructions.Count; i++) + processor.Body.Instructions.Insert(i, instructions[i]); + } + + /// + /// Inserts instructions at the end while also moving Ret down. + /// + /// + /// + public static void InsertLast(this ILProcessor processor, List 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); + } + + /// + /// Inserts instructions before target. + /// + /// + /// + public static void InsertBefore(this ILProcessor processor, Instruction target, List instructions) + { + int index = processor.Body.Instructions.IndexOf(target); + for (int i = 0; i < instructions.Count; i++) + processor.Body.Instructions.Insert(index + i, instructions[i]); + } + + /// + /// Adds instructions to the end of processor. + /// + /// + /// + public static void Add(this ILProcessor processor, List instructions) + { + for (int i = 0; i < instructions.Count; i++) + processor.Body.Instructions.Add(instructions[i]); + } + + /// + /// Inserts instructions before returns. Only works on void types. + /// + /// + /// + public static void InsertBeforeReturns(this ILProcessor processor, CodegenSession session, List 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; + } + } + } + + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/ILProcessorExtensions.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/Extension/ILProcessorExtensions.cs.meta new file mode 100644 index 0000000..dfed3f0 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/ILProcessorExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 820cf8401d4d71c4196dda444559ef8a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/InstructionExtensions.cs b/Assets/FishNet/CodeGenerating/Helpers/Extension/InstructionExtensions.cs new file mode 100644 index 0000000..aefd42c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/InstructionExtensions.cs @@ -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 + { + } + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/InstructionExtensions.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/Extension/InstructionExtensions.cs.meta new file mode 100644 index 0000000..ba87cbb --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/InstructionExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 360667149f16b6c4aba61fd05427cbfb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/MethodDefinitionExtensions.cs b/Assets/FishNet/CodeGenerating/Helpers/Extension/MethodDefinitionExtensions.cs new file mode 100644 index 0000000..373321a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/MethodDefinitionExtensions.cs @@ -0,0 +1,55 @@ +using MonoFN.Cecil; +using MonoFN.Cecil.Cil; + +namespace FishNet.CodeGenerating.Helping.Extension +{ + + internal static class MethodDefinitionExtensions + { + /// + /// Clears the method content and returns ret. + /// + 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().CreateRetDefault(md, importReturnModule)); + } + + /// + /// Returns the ParameterDefinition index from end of parameters. + /// + /// + /// + /// + 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)]; + } + + + /// + /// Creates a variable type within the body and returns it's VariableDef. + /// + internal static VariableDefinition CreateVariable(this MethodDefinition methodDef, TypeReference variableTypeRef) + { + VariableDefinition variableDef = new VariableDefinition(variableTypeRef); + methodDef.Body.Variables.Add(variableDef); + return variableDef; + } + + /// + /// Creates a variable type within the body and returns it's VariableDef. + /// + internal static VariableDefinition CreateVariable(this MethodDefinition methodDef, CodegenSession session, System.Type variableType) + { + return CreateVariable(methodDef, session.GetClass().GetTypeReference(variableType)); + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/MethodDefinitionExtensions.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/Extension/MethodDefinitionExtensions.cs.meta new file mode 100644 index 0000000..a64ef0a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/MethodDefinitionExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 999d4ae4862274f4ba50569c221976dd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/MethodReferenceExtensions.cs b/Assets/FishNet/CodeGenerating/Helpers/Extension/MethodReferenceExtensions.cs new file mode 100644 index 0000000..c55390c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/MethodReferenceExtensions.cs @@ -0,0 +1,155 @@ +using MonoFN.Cecil; +using MonoFN.Cecil.Rocks; +using System; + +namespace FishNet.CodeGenerating.Helping.Extension +{ + + internal static class MethodReferenceExtensions + { + /// + /// Makes a generic method with specified arguments. + /// + /// + /// + /// + public static GenericInstanceMethod MakeGenericMethod(this MethodReference method, params TypeReference[] genericArguments) + { + GenericInstanceMethod result = new GenericInstanceMethod(method); + foreach (TypeReference argument in genericArguments) + result.GenericArguments.Add(argument); + return result; + } + + /// + /// Makes a generic method with the same arguments as the original. + /// + /// + /// + public static GenericInstanceMethod MakeGenericMethod(this MethodReference method) + { + GenericInstanceMethod result = new GenericInstanceMethod(method); + foreach (ParameterDefinition pd in method.Parameters) + result.GenericArguments.Add(pd.ParameterType); + + return result; + } + + /// + /// Returns a method reference for a generic method. + /// + public static MethodReference GetMethodReference(this MethodReference mr, CodegenSession session, TypeReference typeReference) + { + return mr.GetMethodReference(session, new TypeReference[] { typeReference }); + } + + /// + /// Returns a method reference for a generic method. + /// + 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; + } + } + + + /// + /// Gets a Resolve favoring cached results first. + /// + internal static MethodDefinition CachedResolve(this MethodReference methodRef, CodegenSession session) + { + return session.GetClass().GetMethodReferenceResolve(methodRef); + } + + /// + /// 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 + /// Note that calling ArraySegment`T.get_Count directly gives an invalid IL error + /// + /// + /// + /// + public static MethodReference MakeHostInstanceGeneric(this MethodReference self, CodegenSession session, GenericInstanceType instanceType) + { + MethodReference reference = new MethodReference(self.Name, self.ReturnType, instanceType) + { + CallingConvention = self.CallingConvention, + HasThis = self.HasThis, + ExplicitThis = self.ExplicitThis + }; + + foreach (ParameterDefinition parameter in self.Parameters) + reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType)); + + foreach (GenericParameter generic_parameter in self.GenericParameters) + reference.GenericParameters.Add(new GenericParameter(generic_parameter.Name, reference)); + + return session.ImportReference(reference); + } + /// + /// 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 + /// Note that calling ArraySegment`T.get_Count directly gives an invalid IL error + /// + /// + /// + /// + public static MethodReference MakeHostInstanceGeneric(this MethodReference self, TypeReference typeRef, params TypeReference[] args) + { + GenericInstanceType git = typeRef.MakeGenericInstanceType(args); + MethodReference reference = new MethodReference(self.Name, self.ReturnType, git) + { + CallingConvention = self.CallingConvention, + HasThis = self.HasThis, + ExplicitThis = self.ExplicitThis + }; + + foreach (ParameterDefinition parameter in self.Parameters) + reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType)); + + foreach (GenericParameter generic_parameter in self.GenericParameters) + reference.GenericParameters.Add(new GenericParameter(generic_parameter.Name, reference)); + + return reference; + } + public static bool Is(this MethodReference method, string name) + { + return method.DeclaringType.Is() && method.Name == name; + } + public static bool Is(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; + } + + + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/MethodReferenceExtensions.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/Extension/MethodReferenceExtensions.cs.meta new file mode 100644 index 0000000..1a5c3a8 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/MethodReferenceExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ee1c15a06ab386e439ec5aa41e3496f7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/ModuleDefinitionExtensions.cs b/Assets/FishNet/CodeGenerating/Helpers/Extension/ModuleDefinitionExtensions.cs new file mode 100644 index 0000000..e52e04b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/ModuleDefinitionExtensions.cs @@ -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 + { + /// + /// Gets a class within a module. + /// + /// + /// + 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 expression) + { + return ImportReference(moduleDef, (LambdaExpression)expression); + } + public static MethodReference ImportReference(this ModuleDefinition module, Expression> 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 + 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()}"); + } + + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/ModuleDefinitionExtensions.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/Extension/ModuleDefinitionExtensions.cs.meta new file mode 100644 index 0000000..a712e78 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/ModuleDefinitionExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 42648785493390646898f5fa13e1dfd6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/ParameterDefinitionExtensions.cs b/Assets/FishNet/CodeGenerating/Helpers/Extension/ParameterDefinitionExtensions.cs new file mode 100644 index 0000000..542524a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/ParameterDefinitionExtensions.cs @@ -0,0 +1,24 @@ +using MonoFN.Cecil; +using System; + +namespace FishNet.CodeGenerating.Helping.Extension +{ + + internal static class ParameterDefinitionExtensions + { + /// + /// Returns if parameterDef is Type. + /// + /// + /// + /// + public static bool Is(this ParameterDefinition parameterDef, Type type) + { + return parameterDef.ParameterType.FullName == type.FullName; + } + + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/ParameterDefinitionExtensions.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/Extension/ParameterDefinitionExtensions.cs.meta new file mode 100644 index 0000000..bcb2358 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/ParameterDefinitionExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 31071055a2e388141b8f11e1ba4e147e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/TypeDefinitionExtensions.cs b/Assets/FishNet/CodeGenerating/Helpers/Extension/TypeDefinitionExtensions.cs new file mode 100644 index 0000000..f5ea4ff --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/TypeDefinitionExtensions.cs @@ -0,0 +1,521 @@ +using FishNet.CodeGenerating.Extension; +using MonoFN.Cecil; +using MonoFN.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace FishNet.CodeGenerating.Helping.Extension +{ + + + internal static class TypeDefinitionExtensionsOld + { + + /// + /// Creates a GenericInstanceType and adds parameters. + /// + internal static GenericInstanceType CreateGenericInstanceType(this TypeDefinition type, Collection parameters) + { + GenericInstanceType git = new GenericInstanceType(type); + foreach (GenericParameter gp in parameters) + git.GenericArguments.Add(gp); + + return git; + } + + /// + /// Finds public fields in type and base type + /// + /// + /// + public static IEnumerable FindAllPublicFields(this TypeDefinition typeDef, CodegenSession session + , System.Type[] excludedBaseTypes = null, string[] excludedAssemblyPrefixes = null) + { + + GeneralHelper gh = session.GetClass(); + while (typeDef != null) + { + if (IsExcluded(typeDef, excludedBaseTypes, excludedAssemblyPrefixes)) + break; + + foreach (FieldDefinition fd in typeDef.Fields) + { + if (fd.IsStatic) + continue; + if (fd.IsNotSerialized) + continue; + if (gh.CodegenExclude(fd)) + continue; + if (fd.IsPrivate) + continue; + + yield return fd; + } + + try { typeDef = typeDef.BaseType?.CachedResolve(session); } + catch { break; } + } + } + + /// + /// Finds public properties on typeDef and all base types which have a public get/set accessor. + /// + /// + /// + public static IEnumerable FindAllPublicProperties(this TypeDefinition typeDef, CodegenSession session + , System.Type[] excludedBaseTypes = null, string[] excludedAssemblyPrefixes = null) + { + GeneralHelper gh = session.GetClass(); + while (typeDef != null) + { + if (IsExcluded(typeDef, excludedBaseTypes, excludedAssemblyPrefixes)) + break; + + foreach (PropertyDefinition pd in typeDef.Properties) + { + //Missing get or set method. + if (pd.GetMethod == null || pd.SetMethod == null) + continue; + if (gh.CodegenExclude(pd)) + continue; + if (pd.GetMethod.IsPrivate) + continue; + if (pd.SetMethod.IsPrivate) + continue; + if (pd.GetMethod.ReturnType.IsGenericParameter) + continue; + + yield return pd; + } + + try { typeDef = typeDef.BaseType?.CachedResolve(session); } + catch { break; } + } + } + + /// + /// Returns if typeDef is excluded. + /// + private static bool IsExcluded(TypeDefinition typeDef, System.Type[] excludedBaseTypes = null, string[] excludedAssemblyPrefixes = null) + { + if (excludedBaseTypes != null) + { + foreach (System.Type t in excludedBaseTypes) + { + if (typeDef.FullName == t.FullName) + return true; + } + } + if (excludedAssemblyPrefixes != null) + { + foreach (string s in excludedAssemblyPrefixes) + { + int len = s.Length; + string tdAsmName = typeDef.Module.Assembly.FullName; + if (tdAsmName.Length >= len && tdAsmName.Substring(0, len).ToLower() == s.ToLower()) + return true; + } + } + + //Fall through, not excluded. + return false; + } + + + /// + /// Returns if typeDef is excluded. + /// + 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; + } + + /// + /// Returns if typeDef or any of it's parents inherit from NetworkBehaviour. + /// + /// + /// + internal static bool InheritsNetworkBehaviour(this TypeDefinition typeDef, CodegenSession session) + { + string nbFullName = session.GetClass().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; + } + + /// + /// Returns a nested TypeDefinition of name. + /// + internal static TypeDefinition GetNestedType(this TypeDefinition typeDef, string name) + { + foreach (TypeDefinition nestedTd in typeDef.NestedTypes) + { + if (nestedTd.Name == name) + return nestedTd; + } + + return null; + } + + /// + /// Returns if the BaseType for TypeDef exist and is not NetworkBehaviour, + /// + /// + /// + internal static bool CanProcessBaseType(this TypeDefinition typeDef, CodegenSession session) + { + return (typeDef != null && typeDef.BaseType != null && typeDef.BaseType.FullName != session.GetClass().FullName); + } + /// + /// Returns if the BaseType for TypeDef exist and is not NetworkBehaviour, + /// + /// + /// + internal static TypeDefinition GetNextBaseClassToProcess(this TypeDefinition typeDef, CodegenSession session) + { + if (typeDef.BaseType != null && typeDef.BaseType.FullName != session.GetClass().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; + } + + /// + /// Searches for a type in current and inherited types. + /// + 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; + } + + /// + /// Searches for a type in current and inherited types. + /// + 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; + } + + + + /// + /// Returns if typeDef is static (abstract, sealed). + /// + 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)); + } + + /// + /// Gets an enum underlying type for typeDef. + /// + /// + /// + 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}"); + } + + /// + /// Returns if typeDef is derived from type. + /// + /// + /// + /// + internal static bool InheritsFrom(this TypeDefinition typeDef, CodegenSession session) + { + return typeDef.InheritsFrom(session, typeof(T)); + } + + /// + /// Returns if typeDef is derived from type. + /// + /// + /// + /// + 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; + } + + /// + /// Adds a method to typeDef. + /// + /// + /// + /// + /// + internal static MethodDefinition AddMethod(this TypeDefinition typDef, string methodName, MethodAttributes attributes) + { + return AddMethod(typDef, methodName, attributes, typDef.Module.ImportReference(typeof(void))); + } + /// + /// Adds a method to typeDef. + /// + /// + /// + /// + /// + /// + 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; + } + + + /// + /// Finds the first method by a given name. + /// + /// + /// + /// + internal static MethodDefinition GetMethod(this TypeDefinition typeDef, string methodName) + { + return typeDef.Methods.FirstOrDefault(method => method.Name == methodName); + } + + /// + /// Finds the first method by a given name. + /// + /// + /// + /// + internal static MethodDefinition GetMethod(this TypeDefinition typeDef, string methodName, Type[] types) + { + throw new NotImplementedException(); + } + + /// + /// Returns if a type is a subclass of another. + /// + /// + /// + /// + 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; + } + + /// + /// Gets a field reference by name. + /// + /// + /// + /// + 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; + } + + + /// + /// Returns if the TypeDefinition implements TInterface. + /// + /// + /// + /// + public static bool ImplementsInterface(this TypeDefinition typeDef) + { + for (int i = 0; i < typeDef.Interfaces.Count; i++) + { + if (typeDef.Interfaces[i].InterfaceType.Is()) + return true; + } + + return false; + } + + /// + /// Returns if the TypeDefinition implements TInterface. + /// + /// + /// + /// + 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; + } + + + + + /// + /// Returns if the TypeDefinition implements TInterface. + /// + /// + /// + /// + public static bool ImplementsInterfaceRecursive(this TypeDefinition typeDef, CodegenSession session) + { + TypeDefinition climbTypeDef = typeDef; + + while (climbTypeDef != null) + { + if (climbTypeDef.Interfaces.Any(i => i.InterfaceType.Is())) + 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; + } + + /// + /// Returns if the TypeDefinition implements TInterface. + /// + /// + /// + /// + 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; + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/TypeDefinitionExtensions.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/Extension/TypeDefinitionExtensions.cs.meta new file mode 100644 index 0000000..da8687a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/TypeDefinitionExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 645e49fe7eeff3a4e9eb65d77fc6e2ca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/TypeReferenceExtensions.cs b/Assets/FishNet/CodeGenerating/Helpers/Extension/TypeReferenceExtensions.cs new file mode 100644 index 0000000..6b4112e --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/TypeReferenceExtensions.cs @@ -0,0 +1,138 @@ +using MonoFN.Cecil; +using MonoFN.Cecil.Rocks; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace FishNet.CodeGenerating.Helping.Extension +{ + + internal static class TypeReferenceExtensionsOld + { + + /// + /// Gets a Resolve favoring cached results first. + /// + internal static TypeDefinition CachedResolve(this TypeReference typeRef, CodegenSession session) + { + return session.GetClass().GetTypeReferenceResolve(typeRef); + } + + /// + /// Returns if typeRef is a class or struct. + /// + internal static bool IsClassOrStruct(this TypeReference typeRef, CodegenSession session) + { + TypeDefinition typeDef = typeRef.CachedResolve(session); + return (!typeDef.IsPrimitive && !typeDef.IsEnum && (typeDef.IsClass || typeDef.IsValueType)); + } + + /// + /// Returns all properties on typeRef and all base types which have a public get/set accessor. + /// + /// + /// + public static IEnumerable FindAllSerializableProperties(this TypeReference typeRef, CodegenSession session, System.Type[] excludedBaseTypes = null, string[] excludedAssemblyPrefixes = null) + { + return typeRef.CachedResolve(session).FindAllPublicProperties(session, excludedBaseTypes, excludedAssemblyPrefixes); + } + + + /// + /// Gets all public fields in typeRef and base type. + /// + /// + /// + public static IEnumerable FindAllSerializableFields(this TypeReference typeRef, CodegenSession session, + System.Type[] excludedBaseTypes = null, string[] excludedAssemblyPrefixes = null) + { + return typeRef.Resolve().FindAllPublicFields(session, excludedBaseTypes, excludedAssemblyPrefixes); + } + + + /// + /// Returns if a typeRef is type. + /// + /// + /// + /// + public static bool IsType(this TypeReference typeRef, Type type) + { + if (type.IsGenericType) + return typeRef.GetElementType().FullName == type.FullName; + else + return typeRef.FullName == type.FullName; + } + + + + /// + /// Returns if typeRef is a multidimensional array. + /// + /// + /// + public static bool IsMultidimensionalArray(this TypeReference typeRef) + { + return typeRef is ArrayType arrayType && arrayType.Rank > 1; + } + + + /// + /// Returns if typeRef can be resolved. + /// + /// + /// + 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; + } + + /// + /// Creates a generic type out of another type, if needed. + /// + /// + /// + 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; + } + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/TypeReferenceExtensions.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/Extension/TypeReferenceExtensions.cs.meta new file mode 100644 index 0000000..5edaeb9 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/TypeReferenceExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2344f5ab0fda07b498c03fbe0e082c14 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs b/Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs new file mode 100644 index 0000000..6e18a9e --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs @@ -0,0 +1,1412 @@ +using FishNet.CodeGenerating.Extension; +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.CodeGenerating.ILCore; +using FishNet.Managing; +using FishNet.Managing.Logging; +using FishNet.Object; +using FishNet.Object.Helping; +using FishNet.Serializing; +using FishNet.Serializing.Helping; +using MonoFN.Cecil; +using MonoFN.Cecil.Cil; +using MonoFN.Cecil.Rocks; +using System; +using System.Collections.Generic; +using UnityEngine; +using SR = System.Reflection; + +namespace FishNet.CodeGenerating.Helping +{ + internal class GeneralHelper : CodegenBase + { + #region Reflection references. + public string CodegenExcludeAttribute_FullName; + public string CodegenIncludeAttribute_FullName; + public MethodReference Extension_Attribute_Ctor_MethodRef; + public MethodReference Queue_Enqueue_MethodRef; + public MethodReference Queue_get_Count_MethodRef; + public MethodReference Queue_Dequeue_MethodRef; + public MethodReference Queue_Clear_MethodRef; + public TypeReference List_TypeRef; + public MethodReference List_Clear_MethodRef; + public MethodReference List_get_Item_MethodRef; + public MethodReference List_get_Count_MethodRef; + public MethodReference List_Add_MethodRef; + public MethodReference List_RemoveRange_MethodRef; + public MethodReference InstanceFinder_NetworkManager_MethodRef; + public MethodReference NetworkBehaviour_CanLog_MethodRef; + public MethodReference NetworkBehaviour_NetworkManager_MethodRef; + public MethodReference NetworkManager_LogCommon_MethodRef; + public MethodReference NetworkManager_LogWarning_MethodRef; + public MethodReference NetworkManager_LogError_MethodRef; + public MethodReference Debug_LogCommon_MethodRef; + public MethodReference Debug_LogWarning_MethodRef; + public MethodReference Debug_LogError_MethodRef; + public MethodReference IsServer_MethodRef; + public MethodReference IsClient_MethodRef; + public MethodReference NetworkObject_Deinitializing_MethodRef; + public MethodReference Application_IsPlaying_MethodRef; + public string NonSerialized_Attribute_FullName; + public string Single_FullName; + public TypeReference FunctionT2TypeRef; + public TypeReference FunctionT3TypeRef; + public MethodReference FunctionT2ConstructorMethodRef; + public MethodReference FunctionT3ConstructorMethodRef; + //GeneratedComparer + public MethodReference GeneratedComparer_Compare_Set_MethodRef; + public MethodReference GeneratedComparer_IsDefault_Set_MethodRef; + public TypeReference GeneratedComparer_TypeRef; + public TypeDefinition GeneratedComparer_ClassTypeDef; + public MethodDefinition GeneratedComparer_OnLoadMethodDef; + public TypeReference IEquatable_TypeRef; + //Actions. + public TypeReference ActionT2_TypeRef; + public TypeReference ActionT3_TypeRef; + public MethodReference ActionT2Constructor_MethodRef; + public MethodReference ActionT3Constructor_MethodRef; + + private Dictionary _importedTypeReferences = new Dictionary(); + private Dictionary _importedFieldReferences = new Dictionary(); + private Dictionary _methodReferenceResolves = new Dictionary(); + private Dictionary _typeReferenceResolves = new Dictionary(); + private Dictionary _fieldReferenceResolves = new Dictionary(); + private Dictionary _comparerDelegates = new Dictionary(); + #endregion + + #region Const. + public const string UNITYENGINE_ASSEMBLY_PREFIX = "UnityEngine."; + #endregion + + public override bool ImportReferences() + { + Type tmpType; + TypeReference tmpTr; + SR.MethodInfo tmpMi; + SR.PropertyInfo tmpPi; + + NonSerialized_Attribute_FullName = typeof(NonSerializedAttribute).FullName; + Single_FullName = typeof(float).FullName; + + ActionT2_TypeRef = base.ImportReference(typeof(Action<,>)); + ActionT3_TypeRef = base.ImportReference(typeof(Action<,,>)); + ActionT2Constructor_MethodRef = base.ImportReference(typeof(Action<,>).GetConstructors()[0]); + ActionT3Constructor_MethodRef = base.ImportReference(typeof(Action<,,>).GetConstructors()[0]); + + CodegenExcludeAttribute_FullName = typeof(CodegenExcludeAttribute).FullName; + CodegenIncludeAttribute_FullName = typeof(CodegenIncludeAttribute).FullName; + + tmpType = typeof(Queue<>); + base.ImportReference(tmpType); + tmpMi = tmpType.GetMethod("get_Count"); + Queue_get_Count_MethodRef = base.ImportReference(tmpMi); + foreach (SR.MethodInfo mi in tmpType.GetMethods()) + { + if (mi.Name == nameof(Queue.Enqueue)) + Queue_Enqueue_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(Queue.Dequeue)) + Queue_Dequeue_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(Queue.Clear)) + Queue_Clear_MethodRef = base.ImportReference(mi); + } + + /* MISC */ + // + tmpType = typeof(UnityEngine.Application); + tmpPi = tmpType.GetProperty(nameof(UnityEngine.Application.isPlaying)); + if (tmpPi != null) + Application_IsPlaying_MethodRef = base.ImportReference(tmpPi.GetMethod); + // + tmpType = typeof(System.Runtime.CompilerServices.ExtensionAttribute); + tmpTr = base.ImportReference(tmpType); + Extension_Attribute_Ctor_MethodRef = base.ImportReference(tmpTr.GetConstructor(base.Session)); + + //Networkbehaviour. + Type networkBehaviourType = typeof(NetworkBehaviour); + foreach (SR.MethodInfo methodInfo in networkBehaviourType.GetMethods()) + { + if (methodInfo.Name == nameof(NetworkBehaviour.CanLog)) + NetworkBehaviour_CanLog_MethodRef = base.ImportReference(methodInfo); + } + foreach (SR.PropertyInfo propertyInfo in networkBehaviourType.GetProperties()) + { + if (propertyInfo.Name == nameof(NetworkBehaviour.NetworkManager)) + NetworkBehaviour_NetworkManager_MethodRef = base.ImportReference(propertyInfo.GetMethod); + } + + //Instancefinder. + Type instanceFinderType = typeof(InstanceFinder); + SR.PropertyInfo getNetworkManagerPropertyInfo = instanceFinderType.GetProperty(nameof(InstanceFinder.NetworkManager)); + InstanceFinder_NetworkManager_MethodRef = base.ImportReference(getNetworkManagerPropertyInfo.GetMethod); + + //NetworkManager debug logs. + Type networkManagerType = typeof(NetworkManager); + foreach (SR.MethodInfo methodInfo in networkManagerType.GetMethods()) + { + if (methodInfo.Name == nameof(NetworkManager.Log) && methodInfo.GetParameters().Length == 1) + NetworkManager_LogCommon_MethodRef = base.ImportReference(methodInfo); + else if (methodInfo.Name == nameof(NetworkManager.LogWarning)) + NetworkManager_LogWarning_MethodRef = base.ImportReference(methodInfo); + else if (methodInfo.Name == nameof(NetworkManager.LogError)) + NetworkManager_LogError_MethodRef = base.ImportReference(methodInfo); + } + + //Lists. + tmpType = typeof(List<>); + List_TypeRef = base.ImportReference(tmpType); + SR.MethodInfo lstMi; + lstMi = tmpType.GetMethod("Add"); + List_Add_MethodRef = base.ImportReference(lstMi); + lstMi = tmpType.GetMethod("RemoveRange"); + List_RemoveRange_MethodRef = base.ImportReference(lstMi); + lstMi = tmpType.GetMethod("get_Count"); + List_get_Count_MethodRef = base.ImportReference(lstMi); + lstMi = tmpType.GetMethod("get_Item"); + List_get_Item_MethodRef = base.ImportReference(lstMi); + lstMi = tmpType.GetMethod("Clear"); + List_Clear_MethodRef = base.ImportReference(lstMi); + + //Unity debug logs. + Type debugType = typeof(UnityEngine.Debug); + foreach (SR.MethodInfo methodInfo in debugType.GetMethods()) + { + if (methodInfo.Name == nameof(Debug.LogWarning) && methodInfo.GetParameters().Length == 1) + Debug_LogWarning_MethodRef = base.ImportReference(methodInfo); + else if (methodInfo.Name == nameof(Debug.LogError) && methodInfo.GetParameters().Length == 1) + Debug_LogError_MethodRef = base.ImportReference(methodInfo); + else if (methodInfo.Name == nameof(Debug.Log) && methodInfo.GetParameters().Length == 1) + Debug_LogCommon_MethodRef = base.ImportReference(methodInfo); + } + + Type codegenHelper = typeof(CodegenHelper); + foreach (SR.MethodInfo methodInfo in codegenHelper.GetMethods()) + { + if (methodInfo.Name == nameof(CodegenHelper.NetworkObject_Deinitializing)) + NetworkObject_Deinitializing_MethodRef = base.ImportReference(methodInfo); + else if (methodInfo.Name == nameof(CodegenHelper.IsClient)) + IsClient_MethodRef = base.ImportReference(methodInfo); + else if (methodInfo.Name == nameof(CodegenHelper.IsServer)) + IsServer_MethodRef = base.ImportReference(methodInfo); + } + + //Generic functions. + FunctionT2TypeRef = base.ImportReference(typeof(Func<,>)); + FunctionT3TypeRef = base.ImportReference(typeof(Func<,,>)); + FunctionT2ConstructorMethodRef = base.ImportReference(typeof(Func<,>).GetConstructors()[0]); + FunctionT3ConstructorMethodRef = base.ImportReference(typeof(Func<,,>).GetConstructors()[0]); + + GeneratedComparers(); + + //Sets up for generated comparers. + void GeneratedComparers() + { + GeneralHelper gh = base.GetClass(); + GeneratedComparer_ClassTypeDef = gh.GetOrCreateClass(out _, WriterProcessor.GENERATED_TYPE_ATTRIBUTES, "GeneratedComparers___Internal", null, WriterProcessor.GENERATED_WRITER_NAMESPACE); + bool created; + GeneratedComparer_OnLoadMethodDef = gh.GetOrCreateMethod(GeneratedComparer_ClassTypeDef, out created, WriterProcessor.INITIALIZEONCE_METHOD_ATTRIBUTES, WriterProcessor.INITIALIZEONCE_METHOD_NAME, base.Module.TypeSystem.Void); + if (created) + { + gh.CreateRuntimeInitializeOnLoadMethodAttribute(GeneratedComparer_OnLoadMethodDef); + GeneratedComparer_OnLoadMethodDef.Body.GetILProcessor().Emit(OpCodes.Ret); + } + + System.Type repComparerType = typeof(GeneratedComparer<>); + GeneratedComparer_TypeRef = base.ImportReference(repComparerType); + System.Reflection.PropertyInfo pi; + pi = repComparerType.GetProperty(nameof(GeneratedComparer.Compare)); + GeneratedComparer_Compare_Set_MethodRef = base.ImportReference(pi.GetSetMethod()); + pi = repComparerType.GetProperty(nameof(GeneratedComparer.IsDefault)); + GeneratedComparer_IsDefault_Set_MethodRef = base.ImportReference(pi.GetSetMethod()); + + System.Type iEquatableType = typeof(IEquatable<>); + IEquatable_TypeRef = base.ImportReference(iEquatableType); + } + + return true; + } + + + + #region Resolves. + /// + /// Adds a typeRef to TypeReferenceResolves. + /// + public void AddTypeReferenceResolve(TypeReference typeRef, TypeDefinition typeDef) + { + _typeReferenceResolves[typeRef] = typeDef; + } + + /// + /// Gets a TypeDefinition for typeRef. + /// + public TypeDefinition GetTypeReferenceResolve(TypeReference typeRef) + { + TypeDefinition result; + if (_typeReferenceResolves.TryGetValue(typeRef, out result)) + { + return result; + } + else + { + result = typeRef.Resolve(); + AddTypeReferenceResolve(typeRef, result); + } + + return result; + } + + /// + /// Adds a methodRef to MethodReferenceResolves. + /// + public void AddMethodReferenceResolve(MethodReference methodRef, MethodDefinition methodDef) + { + _methodReferenceResolves[methodRef] = methodDef; + } + + /// + /// Gets a TypeDefinition for typeRef. + /// + public MethodDefinition GetMethodReferenceResolve(MethodReference methodRef) + { + MethodDefinition result; + if (_methodReferenceResolves.TryGetValue(methodRef, out result)) + { + return result; + } + else + { + result = methodRef.Resolve(); + AddMethodReferenceResolve(methodRef, result); + } + + return result; + } + + + /// + /// Adds a fieldRef to FieldReferenceResolves. + /// + public void AddFieldReferenceResolve(FieldReference fieldRef, FieldDefinition fieldDef) + { + _fieldReferenceResolves[fieldRef] = fieldDef; + } + + /// + /// Gets a FieldDefinition for fieldRef. + /// + public FieldDefinition GetFieldReferenceResolve(FieldReference fieldRef) + { + FieldDefinition result; + if (_fieldReferenceResolves.TryGetValue(fieldRef, out result)) + { + return result; + } + else + { + result = fieldRef.Resolve(); + AddFieldReferenceResolve(fieldRef, result); + } + + return result; + } + #endregion + + + /// + /// Makes a method an extension method. + /// + public void MakeExtensionMethod(MethodDefinition md) + { + if (md.Parameters.Count == 0) + { + base.LogError($"Method {md.FullName} cannot be made an extension method because it has no parameters."); + return; + } + + md.Attributes |= (MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig); + CustomAttribute ca = new CustomAttribute(Extension_Attribute_Ctor_MethodRef); + md.CustomAttributes.Add(ca); + } + + /// + /// Returns if typeDef should be ignored. + /// + /// + /// + public bool IgnoreTypeDefinition(TypeDefinition typeDef) + { + foreach (CustomAttribute item in typeDef.CustomAttributes) + { + if (item.AttributeType.FullName == typeof(CodegenExcludeAttribute).FullName) + return true; + } + + return false; + } + + /// + /// Returns if type uses CodegenExcludeAttribute. + /// + public bool CodegenExclude(SR.MethodInfo methodInfo) + { + foreach (SR.CustomAttributeData item in methodInfo.CustomAttributes) + { + if (item.AttributeType == typeof(CodegenExcludeAttribute)) + return true; + } + + return false; + } + + /// + /// Returns if type uses CodegenExcludeAttribute. + /// + public bool CodegenExclude(MethodDefinition methodDef) + { + foreach (CustomAttribute item in methodDef.CustomAttributes) + { + if (item.AttributeType.FullName == CodegenExcludeAttribute_FullName) + return true; + } + + return false; + } + + /// + /// Returns if type uses CodegenExcludeAttribute. + /// + public bool CodegenExclude(FieldDefinition fieldDef) + { + foreach (CustomAttribute item in fieldDef.CustomAttributes) + { + if (item.AttributeType.FullName == CodegenExcludeAttribute_FullName) + return true; + } + + return false; + } + + /// + /// Returns if type uses CodegenIncludeAttribute. + /// + public bool CodegenInclude(FieldDefinition fieldDef) + { + foreach (CustomAttribute item in fieldDef.CustomAttributes) + { + if (item.AttributeType.FullName == CodegenIncludeAttribute_FullName) + return true; + } + + return false; + } + + /// + /// Returns if type uses CodegenExcludeAttribute. + /// + public bool CodegenExclude(PropertyDefinition propDef) + { + foreach (CustomAttribute item in propDef.CustomAttributes) + { + if (item.AttributeType.FullName == CodegenExcludeAttribute_FullName) + return true; + } + + return false; + } + + + /// + /// Returns if type uses CodegenExcludeAttribute. + /// + public bool CodegenInclude(PropertyDefinition propDef) + { + foreach (CustomAttribute item in propDef.CustomAttributes) + { + if (item.AttributeType.FullName == CodegenIncludeAttribute_FullName) + return true; + } + + return false; + } + + + + + /// + /// Calls copiedMd with the assumption md shares the same parameters. + /// + public void CallCopiedMethod(MethodDefinition md, MethodDefinition copiedMd) + { + ILProcessor processor = md.Body.GetILProcessor(); + processor.Emit(OpCodes.Ldarg_0); + foreach (var item in copiedMd.Parameters) + processor.Emit(OpCodes.Ldarg, item); + + MethodReference mr = copiedMd.GetMethodReference(base.Session); + processor.Emit(OpCodes.Call, mr); + + } + + /// + /// Removes countVd from list of dataFd starting at index 0. + /// + public List ListRemoveRange(MethodDefinition methodDef, FieldDefinition dataFd, TypeReference dataTr, VariableDefinition countVd) + { + /* Remove entries which exceed maximum buffer. */ + //Method references for uint/data list: + //get_count, RemoveRange. */ + GenericInstanceType dataListGit; + GetGenericLists(dataTr, out dataListGit); + MethodReference lstDataRemoveRangeMr = base.GetClass().List_RemoveRange_MethodRef.MakeHostInstanceGeneric(base.Session, dataListGit); + + List insts = new List(); + ILProcessor processor = methodDef.Body.GetILProcessor(); + + //Index 1 is the uint, 0 is the data. + insts.Add(processor.Create(OpCodes.Ldarg_0));//this. + insts.Add(processor.Create(OpCodes.Ldfld, dataFd)); + insts.Add(processor.Create(OpCodes.Ldc_I4_0)); + insts.Add(processor.Create(OpCodes.Ldloc, countVd)); + insts.Add(processor.Create(lstDataRemoveRangeMr.GetCallOpCode(base.Session), lstDataRemoveRangeMr)); + + return insts; + } + /// + /// Outputs generic lists for dataTr and uint. + /// + public void GetGenericLists(TypeReference dataTr, out GenericInstanceType lstData) + { + TypeReference listDataTr = base.ImportReference(typeof(List<>)); + lstData = listDataTr.MakeGenericInstanceType(new TypeReference[] { dataTr }); + } + /// + /// Outputs generic lists for dataTr and uint. + /// + public void GetGenericQueues(TypeReference dataTr, out GenericInstanceType queueData) + { + TypeReference queueDataTr = base.ImportReference(typeof(Queue<>)); + queueData = queueDataTr.MakeGenericInstanceType(new TypeReference[] { dataTr }); + } + + /// + /// Copies one method to another while transferring diagnostic paths. + /// + public MethodDefinition CopyIntoNewMethod(MethodDefinition originalMd, string toMethodName, out bool alreadyCreated) + { + TypeDefinition typeDef = originalMd.DeclaringType; + + MethodDefinition md = typeDef.GetOrCreateMethodDefinition(base.Session, toMethodName, originalMd, true, out bool created); + alreadyCreated = !created; + if (alreadyCreated) + return md; + + (md.Body, originalMd.Body) = (originalMd.Body, md.Body); + //Move over all the debugging information + foreach (SequencePoint sequencePoint in originalMd.DebugInformation.SequencePoints) + md.DebugInformation.SequencePoints.Add(sequencePoint); + originalMd.DebugInformation.SequencePoints.Clear(); + + foreach (CustomDebugInformation customInfo in originalMd.CustomDebugInformations) + md.CustomDebugInformations.Add(customInfo); + originalMd.CustomDebugInformations.Clear(); + //Swap debuginformation scope. + (originalMd.DebugInformation.Scope, md.DebugInformation.Scope) = (md.DebugInformation.Scope, originalMd.DebugInformation.Scope); + + return md; + } + + /// + /// Creates the RuntimeInitializeOnLoadMethod attribute for a method. + /// + public void CreateRuntimeInitializeOnLoadMethodAttribute(MethodDefinition methodDef, string loadType = "") + { + TypeReference attTypeRef = GetTypeReference(typeof(RuntimeInitializeOnLoadMethodAttribute)); + foreach (CustomAttribute item in methodDef.CustomAttributes) + { + //Already exist. + if (item.AttributeType.FullName == attTypeRef.FullName) + return; + } + + int parameterRequirement = (loadType.Length == 0) ? 0 : 1; + MethodDefinition constructorMethodDef = attTypeRef.GetConstructor(base.Session, parameterRequirement); + MethodReference constructorMethodRef = base.ImportReference(constructorMethodDef); + CustomAttribute ca = new CustomAttribute(constructorMethodRef); + /* If load type isn't null then it + * has to be passed in as the first argument. */ + if (loadType.Length > 0) + { + Type t = typeof(RuntimeInitializeLoadType); + foreach (UnityEngine.RuntimeInitializeLoadType value in t.GetEnumValues()) + { + if (loadType == value.ToString()) + { + TypeReference tr = base.ImportReference(t); + CustomAttributeArgument arg = new CustomAttributeArgument(tr, value); + ca.ConstructorArguments.Add(arg); + } + } + } + + methodDef.CustomAttributes.Add(ca); + } + + /// + /// Gets the default AutoPackType to use for typeRef. + /// + /// + /// + public AutoPackType GetDefaultAutoPackType(TypeReference typeRef) + { + //Singles are defauled to unpacked. + if (typeRef.FullName == Single_FullName) + return AutoPackType.Unpacked; + else + return AutoPackType.Packed; + } + + /// + /// Gets the InitializeOnce method in typeDef or creates the method should it not exist. + /// + /// + /// + public MethodDefinition GetOrCreateMethod(TypeDefinition typeDef, out bool created, MethodAttributes methodAttr, string methodName, TypeReference returnType) + { + MethodDefinition result = typeDef.GetMethod(methodName); + if (result == null) + { + created = true; + result = new MethodDefinition(methodName, methodAttr, returnType); + typeDef.Methods.Add(result); + } + else + { + created = false; + } + + return result; + } + + + /// + /// Gets a class within moduleDef or creates and returns the class if it does not already exist. + /// + /// + /// + public TypeDefinition GetOrCreateClass(out bool created, TypeAttributes typeAttr, string className, TypeReference baseTypeRef, string namespaceName = WriterProcessor.GENERATED_WRITER_NAMESPACE) + { + if (namespaceName.Length == 0) + namespaceName = FishNetILPP.RUNTIME_ASSEMBLY_NAME; + + TypeDefinition type = base.Module.GetClass(className, namespaceName); + if (type != null) + { + created = false; + return type; + } + else + { + created = true; + type = new TypeDefinition(namespaceName, className, + typeAttr, base.ImportReference(typeof(object))); + //Add base class if specified. + if (baseTypeRef != null) + type.BaseType = base.ImportReference(baseTypeRef); + + base.Module.Types.Add(type); + return type; + } + } + + #region HasNonSerializableAttribute + /// + /// Returns if fieldDef has a NonSerialized attribute. + /// + /// + /// + public bool HasNonSerializableAttribute(FieldDefinition fieldDef) + { + foreach (CustomAttribute customAttribute in fieldDef.CustomAttributes) + { + if (customAttribute.AttributeType.FullName == NonSerialized_Attribute_FullName) + return true; + } + + //Fall through, no matches. + return false; + } + /// + /// Returns if typeDef has a NonSerialized attribute. + /// + /// + /// + public bool HasNonSerializableAttribute(TypeDefinition typeDef) + { + foreach (CustomAttribute customAttribute in typeDef.CustomAttributes) + { + if (customAttribute.AttributeType.FullName == NonSerialized_Attribute_FullName) + return true; + } + + //Fall through, no matches. + return false; + } + #endregion + + /// + /// Gets a TypeReference for a type. + /// + /// + public TypeReference GetTypeReference(Type type) + { + TypeReference result; + if (!_importedTypeReferences.TryGetValue(type, out result)) + { + result = base.ImportReference(type); + _importedTypeReferences.Add(type, result); + } + + return result; + } + + /// + /// Gets a FieldReference for a type. + /// + /// + public FieldReference GetFieldReference(FieldDefinition fieldDef) + { + FieldReference result; + if (!_importedFieldReferences.TryGetValue(fieldDef, out result)) + { + result = base.ImportReference(fieldDef); + _importedFieldReferences.Add(fieldDef, result); + } + + return result; + } + + /// + /// Gets the current constructor for typeDef, or makes a new one if constructor doesn't exist. + /// + /// + /// + public MethodDefinition GetOrCreateConstructor(TypeDefinition typeDef, out bool created, bool makeStatic) + { + // find constructor + MethodDefinition constructorMethodDef = typeDef.GetMethod(".cctor"); + if (constructorMethodDef == null) + constructorMethodDef = typeDef.GetMethod(".ctor"); + + //Constructor already exist. + if (constructorMethodDef != null) + { + if (!makeStatic) + constructorMethodDef.Attributes &= ~MethodAttributes.Static; + + created = false; + } + //Static constructor does not exist yet. + else + { + created = true; + MethodAttributes methodAttr = (MonoFN.Cecil.MethodAttributes.HideBySig | + MonoFN.Cecil.MethodAttributes.SpecialName | + MonoFN.Cecil.MethodAttributes.RTSpecialName); + if (makeStatic) + methodAttr |= MonoFN.Cecil.MethodAttributes.Static; + + //Create a constructor. + constructorMethodDef = new MethodDefinition(".ctor", methodAttr, + typeDef.Module.TypeSystem.Void + ); + + typeDef.Methods.Add(constructorMethodDef); + + //Add ret. + ILProcessor processor = constructorMethodDef.Body.GetILProcessor(); + processor.Emit(OpCodes.Ret); + } + + return constructorMethodDef; + } + + /// + /// Creates a return of boolean type. + /// + /// + /// + public void CreateRetBoolean(ILProcessor processor, bool result) + { + OpCode code = (result) ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0; + processor.Emit(code); + processor.Emit(OpCodes.Ret); + } + + #region Debug logging. + /// + /// Creates instructions to log using a NetworkManager or Unity logging. + /// + /// NetworkManager will be used to log first. If the NetworkManager is unavailable Unity logging will be used. + public List LogMessage(MethodDefinition md, string message, LoggingType loggingType) + { + ILProcessor processor = md.Body.GetILProcessor(); + List instructions = new List(); + if (loggingType == LoggingType.Off) + { + base.LogError($"LogMessage called with LoggingType.Off."); + return instructions; + } + + /* Try to store NetworkManager from base to a variable. + * If the base does not exist, such as not inheriting from NetworkBehaviour, + * or if null because the object is not initialized, then use InstanceFinder to + * retrieve the NetworkManager. Then if NetworkManager was found, perform the log. */ + VariableDefinition networkManagerVd = CreateVariable(processor.Body.Method, typeof(NetworkManager)); + + bool useStatic = (md.IsStatic || !md.DeclaringType.InheritsFrom(base.Session)); + //If does not inherit NB then use InstanceFinder. + if (useStatic) + { + SetNetworkManagerFromInstanceFinder(); + } + //Inherits NB, load from base.NetworkManager. + else + { + instructions.Add(processor.Create(OpCodes.Ldarg_0)); + instructions.Add(processor.Create(OpCodes.Call, NetworkBehaviour_NetworkManager_MethodRef)); + instructions.Add(processor.Create(OpCodes.Stloc, networkManagerVd)); + + //If null from NB then use instancefinder. + Instruction skipSetFromInstanceFinderInst = processor.Create(OpCodes.Nop); + //if (nmVd == null) nmVd = InstanceFinder.NetworkManager. + instructions.Add(processor.Create(OpCodes.Ldloc, networkManagerVd)); + instructions.Add(processor.Create(OpCodes.Brtrue_S, skipSetFromInstanceFinderInst)); + SetNetworkManagerFromInstanceFinder(); + instructions.Add(skipSetFromInstanceFinderInst); + } + + //Sets NetworkManager variable from instancefinder. + void SetNetworkManagerFromInstanceFinder() + { + instructions.Add(processor.Create(OpCodes.Call, InstanceFinder_NetworkManager_MethodRef)); + instructions.Add(processor.Create(OpCodes.Stloc, networkManagerVd)); + } + + VariableDefinition networkManagerIsNullVd = CreateVariable(md, typeof(bool)); + //bool networkManagerIsNull = (networkManager == null); + instructions.Add(processor.Create(OpCodes.Ldloc, networkManagerVd)); + instructions.Add(processor.Create(OpCodes.Ldnull)); + instructions.Add(processor.Create(OpCodes.Ceq)); + instructions.Add(processor.Create(OpCodes.Stloc, networkManagerIsNullVd)); + + /* If (networkManagerIsNull) + * networkManager.Log... + * else + * UnityEngine.Debug.Log... */ + Instruction afterNetworkManagerLogInst = processor.Create(OpCodes.Nop); + Instruction afterUnityLogInst = processor.Create(OpCodes.Nop); + instructions.Add(processor.Create(OpCodes.Ldloc, networkManagerIsNullVd)); + instructions.Add(processor.Create(OpCodes.Brtrue, afterNetworkManagerLogInst)); + instructions.AddRange(LogNetworkManagerMessage(md, networkManagerVd, message, loggingType)); + instructions.Add(processor.Create(OpCodes.Br, afterUnityLogInst)); + instructions.Add(afterNetworkManagerLogInst); + instructions.AddRange(LogUnityDebugMessage(md, message, loggingType)); + instructions.Add(afterUnityLogInst); + + return instructions; + } + + /// + /// Creates instructions to log using NetworkManager without error checking. + /// + public List LogNetworkManagerMessage(MethodDefinition md, VariableDefinition networkManagerVd, string message, LoggingType loggingType) + { + List instructions = new List(); + if (!CanUseLogging(loggingType)) + return instructions; + + ILProcessor processor = md.Body.GetILProcessor(); + + MethodReference methodRef; + if (loggingType == LoggingType.Common) + methodRef = NetworkManager_LogCommon_MethodRef; + else if (loggingType == LoggingType.Warning) + methodRef = NetworkManager_LogWarning_MethodRef; + else + methodRef = NetworkManager_LogError_MethodRef; + + instructions.Add(processor.Create(OpCodes.Ldloc, networkManagerVd)); + instructions.Add(processor.Create(OpCodes.Ldstr, message)); + instructions.Add(processor.Create(OpCodes.Call, methodRef)); + + return instructions; + } + + /// + /// Creates instructions to log using Unity logging. + /// + public List LogUnityDebugMessage(MethodDefinition md, string message, LoggingType loggingType) + { + List instructions = new List(); + if (!CanUseLogging(loggingType)) + return instructions; + + ILProcessor processor = md.Body.GetILProcessor(); + + MethodReference methodRef; + if (loggingType == LoggingType.Common) + methodRef = Debug_LogCommon_MethodRef; + else if (loggingType == LoggingType.Warning) + methodRef = Debug_LogWarning_MethodRef; + else + methodRef = Debug_LogError_MethodRef; + + instructions.Add(processor.Create(OpCodes.Ldstr, message)); + instructions.Add(processor.Create(OpCodes.Call, methodRef)); + return instructions; + } + + /// + /// Returns if logging can be done using a LoggingType. + /// + public bool CanUseLogging(LoggingType lt) + { + if (lt == LoggingType.Off) + { + base.LogError($"Log attempt called with LoggingType.Off."); + return false; + } + + return true; + } + #endregion + + #region CreateVariable / CreateParameter. + /// + /// Creates a parameter within methodDef and returns it's ParameterDefinition. + /// + /// + /// + /// + public ParameterDefinition CreateParameter(MethodDefinition methodDef, TypeDefinition parameterTypeDef, string name = "", ParameterAttributes attributes = ParameterAttributes.None, int index = -1) + { + TypeReference typeRef = methodDef.Module.ImportReference(parameterTypeDef); + return CreateParameter(methodDef, typeRef, name, attributes, index); + } + + /// + /// Creates a parameter within methodDef as the next index, with the same data as passed in parameter definition. + /// + public ParameterDefinition CreateParameter(MethodDefinition methodDef, ParameterDefinition parameterTypeDef) + { + base.ImportReference(parameterTypeDef.ParameterType); + + int currentCount = methodDef.Parameters.Count; + string name = (parameterTypeDef.Name + currentCount); + ParameterDefinition parameterDef = new ParameterDefinition(name, parameterTypeDef.Attributes, parameterTypeDef.ParameterType); + methodDef.Parameters.Add(parameterDef); + + return parameterDef; + } + + /// + /// Creates a parameter within methodDef and returns it's ParameterDefinition. + /// + /// + /// + /// + public ParameterDefinition CreateParameter(MethodDefinition methodDef, TypeReference parameterTypeRef, string name = "", ParameterAttributes attributes = ParameterAttributes.None, int index = -1) + { + int currentCount = methodDef.Parameters.Count; + if (string.IsNullOrEmpty(name)) + name = (parameterTypeRef.Name + currentCount); + ParameterDefinition parameterDef = new ParameterDefinition(name, attributes, parameterTypeRef); + if (index == -1) + methodDef.Parameters.Add(parameterDef); + else + methodDef.Parameters.Insert(index, parameterDef); + return parameterDef; + } + /// + /// Creates a parameter within methodDef and returns it's ParameterDefinition. + /// + /// + /// + /// + public ParameterDefinition CreateParameter(MethodDefinition methodDef, Type parameterType, string name = "", ParameterAttributes attributes = ParameterAttributes.None, int index = -1) + { + return CreateParameter(methodDef, GetTypeReference(parameterType), name, attributes, index); + } + /// + /// Creates a variable type within the body and returns it's VariableDef. + /// + /// + /// + /// + public VariableDefinition CreateVariable(MethodDefinition methodDef, TypeReference variableTypeRef) + { + VariableDefinition variableDef = new VariableDefinition(variableTypeRef); + methodDef.Body.Variables.Add(variableDef); + return variableDef; + } + /// Creates a variable type within the body and returns it's VariableDef. + /// + /// + /// + /// + /// + public VariableDefinition CreateVariable(MethodDefinition methodDef, Type variableType) + { + return CreateVariable(methodDef, GetTypeReference(variableType)); + } + #endregion + + #region SetVariableDef. + /// + /// Initializes variableDef as a new object or collection of typeDef. + /// + /// + /// + /// + public void SetVariableDefinitionFromObject(ILProcessor processor, VariableDefinition variableDef, TypeDefinition typeDef) + { + TypeReference type = variableDef.VariableType; + if (type.IsValueType) + { + // structs are created with Initobj + processor.Emit(OpCodes.Ldloca, variableDef); + processor.Emit(OpCodes.Initobj, type); + } + else if (typeDef.InheritsFrom(base.Session)) + { + MethodReference soCreateInstanceMr = processor.Body.Method.Module.ImportReference(() => UnityEngine.ScriptableObject.CreateInstance()); + GenericInstanceMethod genericInstanceMethod = soCreateInstanceMr.GetElementMethod().MakeGenericMethod(new TypeReference[] { type }); + processor.Emit(OpCodes.Call, genericInstanceMethod); + processor.Emit(OpCodes.Stloc, variableDef); + } + else + { + MethodDefinition constructorMethodDef = type.GetConstructor(base.Session); + if (constructorMethodDef == null) + { + base.LogError($"{type.Name} can't be deserialized because a default constructor could not be found. Create a default constructor or a custom serializer/deserializer."); + return; + } + + MethodReference constructorMethodRef = processor.Body.Method.Module.ImportReference(constructorMethodDef); + processor.Emit(OpCodes.Newobj, constructorMethodRef); + processor.Emit(OpCodes.Stloc, variableDef); + } + } + + /// + /// Assigns value to a VariableDef. + /// + /// + /// + /// + public void SetVariableDefinitionFromInt(ILProcessor processor, VariableDefinition variableDef, int value) + { + processor.Emit(OpCodes.Ldc_I4, value); + processor.Emit(OpCodes.Stloc, variableDef); + } + /// + /// Assigns value to a VariableDef. + /// + /// + /// + /// + public void SetVariableDefinitionFromParameter(ILProcessor processor, VariableDefinition variableDef, ParameterDefinition value) + { + processor.Emit(OpCodes.Ldarg, value); + processor.Emit(OpCodes.Stloc, variableDef); + } + #endregion. + + /// + /// Returns if an instruction is a call to a method. + /// + /// + /// + /// + public bool IsCallToMethod(Instruction instruction, out MethodDefinition calledMethod) + { + if (instruction.OpCode == OpCodes.Call && instruction.Operand is MethodDefinition method) + { + calledMethod = method; + return true; + } + else + { + calledMethod = null; + return false; + } + } + + + /// + /// Returns if a serializer and deserializer exist for typeRef. + /// + /// + /// True to create if missing. + /// + public bool HasSerializerAndDeserializer(TypeReference typeRef, bool create) + { + //Make sure it's imported into current module. + typeRef = base.ImportReference(typeRef); + //Can be serialized/deserialized. + bool hasWriter = base.GetClass().HasSerializer(typeRef, create); + bool hasReader = base.GetClass().HasDeserializer(typeRef, create); + + return (hasWriter && hasReader); + } + + /// + /// Creates a return of default value for methodDef. + /// + /// + public List CreateRetDefault(MethodDefinition methodDef, ModuleDefinition importReturnModule = null) + { + ILProcessor processor = methodDef.Body.GetILProcessor(); + List instructions = new List(); + //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().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; + } + + #region GeneratedComparers + /// + /// Creates an equality comparer for dataTr. + /// + public MethodDefinition CreateEqualityComparer(TypeReference dataTr) + { + bool created; + MethodDefinition comparerMd; + if (!_comparerDelegates.TryGetValue(dataTr.FullName, out comparerMd)) + { + comparerMd = GetOrCreateMethod(GeneratedComparer_ClassTypeDef, out created, WriterProcessor.GENERATED_METHOD_ATTRIBUTES, + $"Comparer___{dataTr.FullName}", base.Module.TypeSystem.Boolean); + + /* Nullables are not yet supported for automatic + * comparers. Let user know they must make their own. */ + if (dataTr.IsGenericInstance)// dataTr.IsNullable(base.Session)) + { + base.LogError($"Equality comparers cannot be automatically generated for generic types. Create a custom comparer for {dataTr.FullName}."); + return null; + } + if (dataTr.IsArray) + { + base.LogError($"Equality comparers cannot be automatically generated for collections. Create a custom comparer for {dataTr.FullName}."); + return null; + } + + RegisterComparerDelegate(comparerMd, dataTr); + CreateComparerMethod(); + CreateComparerDelegate(comparerMd, dataTr); + } + + return comparerMd; + + void CreateComparerMethod() + { + //Add parameters. + ParameterDefinition v0Pd = CreateParameter(comparerMd, dataTr, "value0"); + ParameterDefinition v1Pd = CreateParameter(comparerMd, dataTr, "value1"); + ILProcessor processor = comparerMd.Body.GetILProcessor(); + comparerMd.Body.InitLocals = true; + + /* If type is a Unity type do not try to + * create a comparer other than ref comparer, as Unity will have built in ones. */ + if (dataTr.CachedResolve(base.Session).Module.Name.Contains("UnityEngine")) + { + CreateValueOrReferenceComparer(); + } + /* Generic types must have a comparer created for the + * generic encapulation as well the argument types. */ + else if (dataTr.IsGenericInstance) + { + CreateGenericInstanceComparer(); + //Create a class or struct comparer for the container. + if (!dataTr.IsClassOrStruct(base.Session)) + { + base.Session.LogError($"Generic data type {dataTr} was expected to be in a container but is not."); + return; + } + else + { + CreateClassOrStructComparer(); + } + } + //Class or struct. + else if (dataTr.IsClassOrStruct(base.Session)) + { + CreateClassOrStructComparer(); + } + //Value type. + else if (dataTr.IsValueType) + { + CreateValueOrReferenceComparer(); + } + //Unhandled type. + else + { + base.Session.LogError($"Comparer data type {dataTr.FullName} is unhandled."); + return; + } + + void CreateGenericInstanceComparer() + { + /* Create for arguments first. */ + GenericInstanceType git = dataTr as GenericInstanceType; + if (git == null || git.GenericArguments.Count == 0) + { + base.LogError($"Comparer data is generic but generic type returns null, or has no generic arguments."); + return; + } + foreach (TypeReference tr in git.GenericArguments) + { + TypeReference trImported = base.ImportReference(tr); + CreateEqualityComparer(trImported); + } + } + + + + void CreateClassOrStructComparer() + { + //Class or struct. + Instruction exitMethodInst = processor.Create(OpCodes.Ldc_I4_0); + + //Fields. + foreach (FieldDefinition fieldDef in dataTr.FindAllSerializableFields(base.Session + , null, WriterProcessor.EXCLUDED_ASSEMBLY_PREFIXES)) + { + base.ImportReference(fieldDef); + MethodDefinition recursiveMd = CreateEqualityComparer(fieldDef.FieldType); + if (recursiveMd == null) + break; + processor.Append(GetLoadParameterInstruction(comparerMd, v0Pd)); + processor.Emit(OpCodes.Ldfld, fieldDef); + processor.Append(GetLoadParameterInstruction(comparerMd, v1Pd)); + processor.Emit(OpCodes.Ldfld, fieldDef); + FinishTypeReferenceCompare(fieldDef.FieldType); + } + + //Properties. + foreach (PropertyDefinition propertyDef in dataTr.FindAllSerializableProperties(base.Session + , null, WriterProcessor.EXCLUDED_ASSEMBLY_PREFIXES)) + { + MethodReference getMr = base.Module.ImportReference(propertyDef.GetMethod); + MethodDefinition recursiveMd = CreateEqualityComparer(getMr.ReturnType); + if (recursiveMd == null) + break; + processor.Append(GetLoadParameterInstruction(comparerMd, v0Pd)); + processor.Emit(OpCodes.Call, getMr); + processor.Append(GetLoadParameterInstruction(comparerMd, v1Pd)); + processor.Emit(OpCodes.Call, getMr); + FinishTypeReferenceCompare(propertyDef.PropertyType); + } + + //Return true; + processor.Emit(OpCodes.Ldc_I4_1); + processor.Emit(OpCodes.Ret); + processor.Append(exitMethodInst); + processor.Emit(OpCodes.Ret); + + + void FinishTypeReferenceCompare(TypeReference tr) + { + /* If a class or struct see if it already has a comparer + * using IEquatable. If so then call the comparer method. + * Otherwise make a new comparer and call it. */ + if (tr.IsClassOrStruct(base.Session)) + { + //Make equatable for type. + GenericInstanceType git = IEquatable_TypeRef.MakeGenericInstanceType(tr); + bool createNestedComparer = !tr.CachedResolve(base.Session).ImplementsInterface(git.FullName); + //Create new. + if (createNestedComparer) + { + MethodDefinition cMd = CreateEqualityComparer(tr); + processor.Emit(OpCodes.Call, cMd); + processor.Emit(OpCodes.Brfalse, exitMethodInst); + } + //Call existing. + else + { + MethodDefinition cMd = tr.CachedResolve(base.Session).GetMethod("op_Equality"); + if (cMd == null) + { + base.LogError($"Type {tr.FullName} implements IEquatable but the comparer method could not be found."); + return; + } + else + { + MethodReference mr = base.ImportReference(cMd); + processor.Emit(OpCodes.Call, mr); + processor.Emit(OpCodes.Brfalse, exitMethodInst); + } + } + } + //Value types do not need to check custom comparers. + else + { + processor.Emit(OpCodes.Bne_Un, exitMethodInst); + } + + } + } + + void CreateValueOrReferenceComparer() + { + base.ImportReference(dataTr); + processor.Append(GetLoadParameterInstruction(comparerMd, v0Pd)); + processor.Append(GetLoadParameterInstruction(comparerMd, v1Pd)); + processor.Emit(OpCodes.Ceq); + processor.Emit(OpCodes.Ret); + } + + } + + } + + /// + /// Registers a comparer method. + /// + /// + /// + public void RegisterComparerDelegate(MethodDefinition methodDef, TypeReference dataTr) + { + _comparerDelegates.Add(dataTr.FullName, methodDef); + } + /// + /// Creates a delegate for GeneratedComparers. + /// + public void CreateComparerDelegate(MethodDefinition comparerMd, TypeReference dataTr) + { + dataTr = base.ImportReference(dataTr); + //Initialize delegate for made comparer. + List insts = new List(); + ILProcessor processor = GeneratedComparer_OnLoadMethodDef.Body.GetILProcessor(); + //Create a Func delegate + insts.Add(processor.Create(OpCodes.Ldnull)); + insts.Add(processor.Create(OpCodes.Ldftn, comparerMd)); + + GenericInstanceType git; + git = FunctionT3TypeRef.MakeGenericInstanceType(dataTr, dataTr, GetTypeReference(typeof(bool))); + MethodReference functionConstructorInstanceMethodRef = FunctionT3ConstructorMethodRef.MakeHostInstanceGeneric(base.Session, git); + insts.Add(processor.Create(OpCodes.Newobj, functionConstructorInstanceMethodRef)); + + //Call delegate to ReplicateComparer.Compare(T, T); + git = GeneratedComparer_TypeRef.MakeGenericInstanceType(dataTr); + MethodReference comparerMr = GeneratedComparer_Compare_Set_MethodRef.MakeHostInstanceGeneric(base.Session, git); + insts.Add(processor.Create(OpCodes.Call, comparerMr)); + processor.InsertFirst(insts); + } + + + + /// + /// Returns an OpCode for loading a parameter. + /// + public OpCode GetLoadParameterOpCode(ParameterDefinition pd) + { + TypeReference tr = pd.ParameterType; + return (tr.IsValueType && tr.IsClassOrStruct(base.Session)) ? OpCodes.Ldarga : OpCodes.Ldarg; + } + + /// + /// Returns an instruction for loading a parameter.s + /// + public Instruction GetLoadParameterInstruction(MethodDefinition md, ParameterDefinition pd) + { + ILProcessor processor = md.Body.GetILProcessor(); + OpCode oc = GetLoadParameterOpCode(pd); + return processor.Create(oc, pd); + } + + /// + /// Creates an IsDefault comparer for dataTr. + /// + public void CreateIsDefaultComparer(TypeReference dataTr, MethodDefinition compareMethodDef) + { + GeneralHelper gh = base.GetClass(); + + MethodDefinition isDefaultMd = gh.GetOrCreateMethod(GeneratedComparer_ClassTypeDef, out bool created, WriterProcessor.GENERATED_METHOD_ATTRIBUTES, + $"IsDefault___{dataTr.FullName}", base.Module.TypeSystem.Boolean); + //Already done. This can happen if the same replicate data is used in multiple places. + if (!created) + return; + + MethodReference compareMr = base.ImportReference(compareMethodDef); + CreateIsDefaultMethod(); + CreateIsDefaultDelegate(); + + void CreateIsDefaultMethod() + { + //Add parameters. + ParameterDefinition v0Pd = gh.CreateParameter(isDefaultMd, dataTr, "value0"); + ILProcessor processor = isDefaultMd.Body.GetILProcessor(); + isDefaultMd.Body.InitLocals = true; + + + processor.Emit(OpCodes.Ldarg, v0Pd); + //If a struct. + if (dataTr.IsValueType) + { + //Init a default local. + VariableDefinition defaultVd = gh.CreateVariable(isDefaultMd, dataTr); + processor.Emit(OpCodes.Ldloca, defaultVd); + processor.Emit(OpCodes.Initobj, dataTr); + processor.Emit(OpCodes.Ldloc, defaultVd); + } + //If a class. + else + { + processor.Emit(OpCodes.Ldnull); + } + + processor.Emit(OpCodes.Call, compareMr); + processor.Emit(OpCodes.Ret); + + + } + + //Creates a delegate to compare two of replicateTr. + void CreateIsDefaultDelegate() + { + //Initialize delegate for made comparer. + List insts = new List(); + ILProcessor processor = GeneratedComparer_OnLoadMethodDef.Body.GetILProcessor(); + //Create a Func delegate + insts.Add(processor.Create(OpCodes.Ldnull)); + insts.Add(processor.Create(OpCodes.Ldftn, isDefaultMd)); + + GenericInstanceType git; + git = gh.FunctionT2TypeRef.MakeGenericInstanceType(dataTr, gh.GetTypeReference(typeof(bool))); + MethodReference funcCtorMethodRef = gh.FunctionT2ConstructorMethodRef.MakeHostInstanceGeneric(base.Session, git); + insts.Add(processor.Create(OpCodes.Newobj, funcCtorMethodRef)); + + //Call delegate to ReplicateComparer.IsDefault(T). + git = GeneratedComparer_TypeRef.MakeGenericInstanceType(dataTr); + MethodReference isDefaultMr = GeneratedComparer_IsDefault_Set_MethodRef.MakeHostInstanceGeneric(base.Session, git); + insts.Add(processor.Create(OpCodes.Call, isDefaultMr)); + processor.InsertFirst(insts); + } + + } + #endregion + } +} + diff --git a/Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs.meta new file mode 100644 index 0000000..804c6a0 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6139ff104f3c24442b26dbc4e40d5ce5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/GenericReaderHelper.cs b/Assets/FishNet/CodeGenerating/Helpers/GenericReaderHelper.cs new file mode 100644 index 0000000..c42cb5a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/GenericReaderHelper.cs @@ -0,0 +1 @@ +//Remove on 2024/01/01 \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/GenericReaderHelper.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/GenericReaderHelper.cs.meta new file mode 100644 index 0000000..9fd930e --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/GenericReaderHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8bc93cce5d44d604c805976e696dd7da +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/GenericWriterHelper.cs b/Assets/FishNet/CodeGenerating/Helpers/GenericWriterHelper.cs new file mode 100644 index 0000000..c42cb5a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/GenericWriterHelper.cs @@ -0,0 +1 @@ +//Remove on 2024/01/01 \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/GenericWriterHelper.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/GenericWriterHelper.cs.meta new file mode 100644 index 0000000..87f5a07 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/GenericWriterHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dcb8399b7678ff1429b6e211e6f26d10 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs b/Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs new file mode 100644 index 0000000..95c10a8 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs @@ -0,0 +1,443 @@ +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.CodeGenerating.Processing; +using FishNet.Configuring; +using FishNet.Managing.Logging; +using FishNet.Object; +using FishNet.Object.Delegating; +using FishNet.Object.Helping; +using FishNet.Object.Prediction.Delegating; +using MonoFN.Cecil; +using MonoFN.Cecil.Cil; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace FishNet.CodeGenerating.Helping +{ + internal class NetworkBehaviourHelper : CodegenBase + { + #region Reflection references. + //Names. + internal string FullName; + //Prediction. + public string ClearReplicateCache_Method_Name = nameof(NetworkBehaviour.ClearReplicateCache_Internal); + public MethodReference Replicate_Server_MethodRef; + public MethodReference Replicate_Client_MethodRef; + public MethodReference Replicate_Reader_MethodRef; + public MethodReference Replicate_ExitEarly_A_MethodRef; + public MethodReference Reconcile_ExitEarly_A_MethodRef; + public MethodReference Reconcile_Server_MethodRef; + public MethodReference Reconcile_Client_MethodRef; + public MethodReference Reconcile_Reader_MethodRef; + public MethodReference RegisterReplicateRpc_MethodRef; + public MethodReference RegisterReconcileRpc_MethodRef; + public MethodReference ReplicateRpcDelegateConstructor_MethodRef; + public MethodReference ReconcileRpcDelegateConstructor_MethodRef; + //RPCs. + public MethodReference SendServerRpc_MethodRef; + public MethodReference SendObserversRpc_MethodRef; + public MethodReference SendTargetRpc_MethodRef; + public MethodReference DirtySyncType_MethodRef; + public MethodReference RegisterServerRpc_MethodRef; + public MethodReference RegisterObserversRpc_MethodRef; + public MethodReference RegisterTargetRpc_MethodRef; + public MethodReference ServerRpcDelegateConstructor_MethodRef; + public MethodReference ClientRpcDelegateConstructor_MethodRef; + //Is checks. + public MethodReference IsClient_MethodRef; + public MethodReference IsOwner_MethodRef; + public MethodReference IsServer_MethodRef; + public MethodReference IsHost_MethodRef; + //Misc. + public TypeReference TypeRef; + public MethodReference OwnerMatches_MethodRef; + public MethodReference LocalConnection_MethodRef; + public MethodReference Owner_MethodRef; + public MethodReference ReadSyncVar_MethodRef; + public MethodReference NetworkInitializeIfDisabled_MethodRef; + //TimeManager. + public MethodReference TimeManager_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 networkBehaviourType = typeof(NetworkBehaviour); + TypeRef = base.ImportReference(networkBehaviourType); + FullName = networkBehaviourType.FullName; + base.ImportReference(networkBehaviourType); + + //ServerRpcDelegate and ClientRpcDelegate constructors. + ServerRpcDelegateConstructor_MethodRef = base.ImportReference(typeof(ServerRpcDelegate).GetConstructors().First()); + ClientRpcDelegateConstructor_MethodRef = base.ImportReference(typeof(ClientRpcDelegate).GetConstructors().First()); + //Prediction Rpc delegate constructors. + ReplicateRpcDelegateConstructor_MethodRef = base.ImportReference(typeof(ReplicateRpcDelegate).GetConstructors().First()); + ReconcileRpcDelegateConstructor_MethodRef = base.ImportReference(typeof(ReconcileRpcDelegate).GetConstructors().First()); + + foreach (MethodInfo mi in networkBehaviourType.GetMethods((BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic))) + { + //CreateDelegates. + if (mi.Name == nameof(NetworkBehaviour.RegisterServerRpc_Internal)) + RegisterServerRpc_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.RegisterObserversRpc_Internal)) + RegisterObserversRpc_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.RegisterTargetRpc_Internal)) + RegisterTargetRpc_MethodRef = base.ImportReference(mi); + //SendPredictions. + else if (mi.Name == nameof(NetworkBehaviour.RegisterReplicateRpc_Internal)) + RegisterReplicateRpc_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.RegisterReconcileRpc_Internal)) + RegisterReconcileRpc_MethodRef = base.ImportReference(mi); + //SendRpcs. + else if (mi.Name == nameof(NetworkBehaviour.SendServerRpc_Internal)) + SendServerRpc_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.SendObserversRpc_Internal)) + SendObserversRpc_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.SendTargetRpc_Internal)) + SendTargetRpc_MethodRef = base.ImportReference(mi); + //Prediction. + else if (mi.Name == nameof(NetworkBehaviour.Replicate_ExitEarly_A_Internal)) + Replicate_ExitEarly_A_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.Replicate_Server_Internal)) + Replicate_Server_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.Replicate_Reader_Internal)) + Replicate_Reader_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.Reconcile_Reader_Internal)) + Reconcile_Reader_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.Reconcile_ExitEarly_A_Internal)) + Reconcile_ExitEarly_A_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.Reconcile_Server_Internal)) + Reconcile_Server_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.Replicate_Client_Internal)) + Replicate_Client_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.Reconcile_Client_Internal)) + Reconcile_Client_MethodRef = base.ImportReference(mi); + //Misc. + else if (mi.Name == nameof(NetworkBehaviour.OwnerMatches)) + OwnerMatches_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.ReadSyncVar)) + ReadSyncVar_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.DirtySyncType)) + DirtySyncType_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.NetworkInitializeIfDisabled)) + NetworkInitializeIfDisabled_MethodRef = base.ImportReference(mi); + } + + foreach (PropertyInfo pi in networkBehaviourType.GetProperties((BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic))) + { + //Server/Client states. + if (pi.Name == nameof(NetworkBehaviour.IsClient)) + IsClient_MethodRef = base.ImportReference(pi.GetMethod); + else if (pi.Name == nameof(NetworkBehaviour.IsServer)) + IsServer_MethodRef = base.ImportReference(pi.GetMethod); + else if (pi.Name == nameof(NetworkBehaviour.IsHost)) + IsHost_MethodRef = base.ImportReference(pi.GetMethod); + else if (pi.Name == nameof(NetworkBehaviour.IsOwner)) + IsOwner_MethodRef = base.ImportReference(pi.GetMethod); + //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; + } + + /// + /// Returnsthe child most Awake by iterating up childMostTypeDef. + /// + /// + /// + /// + internal MethodDefinition GetAwakeMethodDefinition(TypeDefinition typeDef) + { + return typeDef.GetMethod(AWAKE_METHOD_NAME); + } + + + /// + /// Creates a replicate delegate. + /// + /// + /// + /// + /// + 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 insts = new List(); + 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); + } + + + + /// + /// Creates a RPC delegate for rpcType. + /// + /// + /// + /// + /// + 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 insts = new List(); + 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, ServerRpcDelegateConstructor_MethodRef)); + insts.Add(processor.Create(OpCodes.Call, RegisterServerRpc_MethodRef)); + } + //Observers. + else if (rpcType == RpcType.Observers) + { + insts.Add(processor.Create(OpCodes.Newobj, ClientRpcDelegateConstructor_MethodRef)); + insts.Add(processor.Create(OpCodes.Call, RegisterObserversRpc_MethodRef)); + } + //Target + else if (rpcType == RpcType.Target) + { + insts.Add(processor.Create(OpCodes.Newobj, ClientRpcDelegateConstructor_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); + } + + /// + /// Creates exit method condition if local client is not owner. + /// + /// True if to ret when owner, false to ret when not owner. + /// Returns Ret instruction. + internal Instruction CreateLocalClientIsOwnerCheck(MethodDefinition methodDef, LoggingType loggingType, bool notifyMessageCanBeDisabled, bool retIfOwner, bool insertFirst) + { + List instructions = new List(); + /* 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().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; + } + + /// + /// Creates exit method condition if remote client is not owner. + /// + /// + 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; + } + + /// + /// Creates exit method condition if not client. + /// + /// + /// + /// + internal void CreateIsClientCheck(MethodDefinition methodDef, LoggingType loggingType, bool useStatic, bool insertFirst) + { + /* 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 instructions = new List(); + //Checking against the NetworkObject. + if (!useStatic) + { + instructions.Add(processor.Create(OpCodes.Ldarg_0)); //argument: this + //If (!base.IsClient) + instructions.Add(processor.Create(OpCodes.Call, IsClient_MethodRef)); + } + //Checking instanceFinder. + else + { + instructions.Add(processor.Create(OpCodes.Call, base.GetClass().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 or if it does not contain a NetworkObject component. {DISABLE_LOGGING_TEXT}."; + instructions.AddRange(base.GetClass().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); + } + } + + + /// + /// Creates exit method condition if not server. + /// + /// + /// + internal void CreateIsServerCheck(MethodDefinition methodDef, LoggingType loggingType, bool useStatic, bool insertFirst) + { + /* 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 instructions = new List(); + if (!useStatic) + { + instructions.Add(processor.Create(OpCodes.Ldarg_0)); //argument: this + //If (!base.IsServer) + instructions.Add(processor.Create(OpCodes.Call, IsServer_MethodRef)); + } + //Checking instanceFinder. + else + { + instructions.Add(processor.Create(OpCodes.Call, base.GetClass().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 or if it does not contain a NetworkObject component. {DISABLE_LOGGING_TEXT}"; + instructions.AddRange(base.GetClass().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); + } + } + + /// + /// Creates a return using the ReturnType for methodDef. + /// + /// + /// + /// + public List CreateRetDefault(MethodDefinition methodDef, ModuleDefinition importReturnModule = null) + { + ILProcessor processor = methodDef.Body.GetILProcessor(); + List instructions = new List(); + //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().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; + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs.meta new file mode 100644 index 0000000..55c1c3c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0c42e06349d6890459a155a2afe17d41 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/NetworkConnectionHelper.cs b/Assets/FishNet/CodeGenerating/Helpers/NetworkConnectionHelper.cs new file mode 100644 index 0000000..2111ff4 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/NetworkConnectionHelper.cs @@ -0,0 +1 @@ +//Remove on 2023/06/01 \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/NetworkConnectionHelper.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/NetworkConnectionHelper.cs.meta new file mode 100644 index 0000000..f5985b9 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/NetworkConnectionHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bcbcb288008d6da4eab7a5279dd6b4f8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/NetworkConnectionImports.cs b/Assets/FishNet/CodeGenerating/Helpers/NetworkConnectionImports.cs new file mode 100644 index 0000000..30871f1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/NetworkConnectionImports.cs @@ -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; + } + + } +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/NetworkConnectionImports.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/NetworkConnectionImports.cs.meta new file mode 100644 index 0000000..f8b4ff2 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/NetworkConnectionImports.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6d061dda8ed87ed48a08020942ad63f6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/ObjectHelper.cs b/Assets/FishNet/CodeGenerating/Helpers/ObjectHelper.cs new file mode 100644 index 0000000..f4adf76 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/ObjectHelper.cs @@ -0,0 +1,94 @@ +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 SyncList_Name; + public string SyncDictionary_Name; + public string SyncHashSet_Name; + //Is checks. + public MethodReference InstanceFinder_IsServer_MethodRef; + public MethodReference InstanceFinder_IsClient_MethodRef; + //Misc. + public TypeReference NetworkBehaviour_TypeRef; + public MethodReference NetworkConnection_IsValid_MethodRef; + public MethodReference NetworkConnection_IsActive_MethodRef; + public MethodReference Dictionary_Add_UShort_SyncBase_MethodRef; + public MethodReference NetworkConnection_GetIsLocalClient_MethodRef; + #endregion + + public override bool ImportReferences() + { + Type tmpType; + /* SyncObject names. */ + //SyncList. + tmpType = typeof(SyncList<>); + base.ImportReference(tmpType); + SyncList_Name = tmpType.Name; + //SyncDictionary. + tmpType = typeof(SyncDictionary<,>); + base.ImportReference(tmpType); + SyncDictionary_Name = tmpType.Name; + //SyncHashSet. + tmpType = typeof(SyncHashSet<>); + base.ImportReference(tmpType); + SyncHashSet_Name = tmpType.Name; + + NetworkBehaviour_TypeRef = base.ImportReference(typeof(NetworkBehaviour)); + + tmpType = typeof(NetworkConnection); + TypeReference networkConnectionTr = base.ImportReference(tmpType); + foreach (PropertyDefinition item in networkConnectionTr.CachedResolve(base.Session).Properties) + { + if (item.Name == nameof(NetworkConnection.IsLocalClient)) + NetworkConnection_GetIsLocalClient_MethodRef = base.ImportReference(item.GetMethod); + } + + //Dictionary.Add(ushort, SyncBase). + Type dictType = typeof(Dictionary); + 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.Add)) + { + Dictionary_Add_UShort_SyncBase_MethodRef = base.ImportReference(item); + break; + } + } + + //InstanceFinder infos. + Type instanceFinderType = typeof(InstanceFinder); + foreach (PropertyInfo pi in instanceFinderType.GetProperties()) + { + if (pi.Name == nameof(InstanceFinder.IsClient)) + InstanceFinder_IsClient_MethodRef = base.ImportReference(pi.GetMethod); + else if (pi.Name == nameof(InstanceFinder.IsServer)) + InstanceFinder_IsServer_MethodRef = base.ImportReference(pi.GetMethod); + } + + //NetworkConnection. + foreach (PropertyInfo pi in typeof(NetworkConnection).GetProperties((BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic))) + { + if (pi.Name == nameof(NetworkConnection.IsValid)) + NetworkConnection_IsValid_MethodRef = base.ImportReference(pi.GetMethod); + else if (pi.Name == nameof(NetworkConnection.IsActive)) + NetworkConnection_IsActive_MethodRef = base.ImportReference(pi.GetMethod); + } + + return true; + } + + } +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/ObjectHelper.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/ObjectHelper.cs.meta new file mode 100644 index 0000000..4e50b5f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/ObjectHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 033da35314653aa4689b4582e7929295 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/PhysicsHelper.cs b/Assets/FishNet/CodeGenerating/Helpers/PhysicsHelper.cs new file mode 100644 index 0000000..25224b0 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/PhysicsHelper.cs @@ -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; + } + + + /// + /// Returns instructions to get a physics scene from a gameObject. + /// + public List GetPhysicsScene(MethodDefinition md, VariableDefinition gameObjectVd, bool threeDimensional) + { + ILProcessor processor = md.Body.GetILProcessor(); + return GetPhysicsScene(processor, gameObjectVd, threeDimensional); + } + + /// + /// Returns instructions to get a physics scene from a gameObject. + /// + public List GetPhysicsScene(ILProcessor processor, VariableDefinition gameObjectVd, bool threeDimensional) + { + List insts = new List(); + + //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; + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/PhysicsHelper.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/PhysicsHelper.cs.meta new file mode 100644 index 0000000..1f43ee9 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/PhysicsHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 28ae27f7bc8e89547a606262508fdd36 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/PredictedObjectHelper.cs b/Assets/FishNet/CodeGenerating/Helpers/PredictedObjectHelper.cs new file mode 100644 index 0000000..b8e119d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/PredictedObjectHelper.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/PredictedObjectHelper.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/PredictedObjectHelper.cs.meta new file mode 100644 index 0000000..ff446ca --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/PredictedObjectHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e9a06c812bf785a44a38a5852ff866d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/ReaderGenerator.cs b/Assets/FishNet/CodeGenerating/Helpers/ReaderGenerator.cs new file mode 100644 index 0000000..c42cb5a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/ReaderGenerator.cs @@ -0,0 +1 @@ +//Remove on 2024/01/01 \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/ReaderGenerator.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/ReaderGenerator.cs.meta new file mode 100644 index 0000000..9ff807d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/ReaderGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a8afc0f62ceeaee45aa496ba5650d010 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/ReaderHelper.cs b/Assets/FishNet/CodeGenerating/Helpers/ReaderHelper.cs new file mode 100644 index 0000000..c42cb5a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/ReaderHelper.cs @@ -0,0 +1 @@ +//Remove on 2024/01/01 \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/ReaderHelper.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/ReaderHelper.cs.meta new file mode 100644 index 0000000..00c2968 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/ReaderHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 07c42037569e53b4aa6701adefd3e063 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs b/Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs new file mode 100644 index 0000000..4fe91b8 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs @@ -0,0 +1,71 @@ +using FishNet.Connection; +using FishNet.Serializing; +using MonoFN.Cecil; +using System; +using System.Reflection; + +namespace FishNet.CodeGenerating.Helping +{ + internal class ReaderImports : CodegenBase + { + #region Reflection references. + public TypeReference PooledReader_TypeRef; + public TypeReference Reader_TypeRef; + public TypeReference NetworkConnection_TypeRef; + public MethodReference PooledReader_ReadNetworkBehaviour_MethodRef; + public MethodReference Reader_ReadPackedWhole_MethodRef; + public MethodReference Reader_ReadDictionary_MethodRef; + public MethodReference Reader_ReadList_MethodRef; + public MethodReference Reader_ReadListCache_MethodRef; + public MethodReference Reader_ReadArray_MethodRef; + public TypeReference GenericReaderTypeRef; + public TypeReference ReaderTypeRef; + public MethodReference ReadSetMethodRef; + public MethodReference ReadAutoPackSetMethodRef; + #endregion + + /// + /// Imports references needed by this helper. + /// + /// + /// + public override bool ImportReferences() + { + ReaderProcessor rp = base.GetClass(); + + PooledReader_TypeRef = base.ImportReference(typeof(PooledReader)); + Reader_TypeRef = base.ImportReference(typeof(Reader)); + NetworkConnection_TypeRef = base.ImportReference(typeof(NetworkConnection)); + + GenericReaderTypeRef = base.ImportReference(typeof(GenericReader<>)); + ReaderTypeRef = base.ImportReference(typeof(Reader)); + + System.Reflection.PropertyInfo readPropertyInfo; + readPropertyInfo = typeof(GenericReader<>).GetProperty(nameof(GenericReader.Read)); + ReadSetMethodRef = base.ImportReference(readPropertyInfo.GetSetMethod()); + readPropertyInfo = typeof(GenericReader<>).GetProperty(nameof(GenericReader.ReadAutoPack)); + ReadAutoPackSetMethodRef = base.ImportReference(readPropertyInfo.GetSetMethod()); + + + Type pooledReaderType = typeof(PooledReader); + foreach (MethodInfo methodInfo in pooledReaderType.GetMethods()) + { + int parameterCount = methodInfo.GetParameters().Length; + /* Special methods. */ + if (methodInfo.Name == nameof(PooledReader.ReadPackedWhole)) + Reader_ReadPackedWhole_MethodRef = base.ImportReference(methodInfo); + //Relay readers. + else if (parameterCount == 0 && methodInfo.Name == nameof(PooledReader.ReadDictionary)) + Reader_ReadDictionary_MethodRef = base.ImportReference(methodInfo); + else if (parameterCount == 0 && methodInfo.Name == nameof(PooledReader.ReadListAllocated)) + Reader_ReadList_MethodRef = base.ImportReference(methodInfo); + else if (parameterCount == 0 && methodInfo.Name == nameof(PooledReader.ReadListCacheAllocated)) + Reader_ReadListCache_MethodRef = base.ImportReference(methodInfo); + else if (parameterCount == 0 && methodInfo.Name == nameof(PooledReader.ReadArrayAllocated)) + Reader_ReadArray_MethodRef = base.ImportReference(methodInfo); + } + + return true; + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs.meta new file mode 100644 index 0000000..e491410 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 350861dcbcbabc447acd37e4310b0697 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/TimeManagerHelper.cs b/Assets/FishNet/CodeGenerating/Helpers/TimeManagerHelper.cs new file mode 100644 index 0000000..21f2106 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/TimeManagerHelper.cs @@ -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; + } + + + } +} diff --git a/Assets/FishNet/CodeGenerating/Helpers/TimeManagerHelper.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/TimeManagerHelper.cs.meta new file mode 100644 index 0000000..a247b73 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/TimeManagerHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 11dbcc0798e079e4a85fe98dfc9fe23a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/TransportHelper.cs b/Assets/FishNet/CodeGenerating/Helpers/TransportHelper.cs new file mode 100644 index 0000000..4816ab4 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/TransportHelper.cs @@ -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 + + /// + /// Resets cached values. + /// + private void ResetValues() + { + Channel_TypeRef = null; + } + + + /// + /// Imports references needed by this helper. + /// + /// + /// + public override bool ImportReferences() + { + ResetValues(); + + Channel_TypeRef = base.ImportReference(typeof(Channel)); + + return true; + } + + } +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/TransportHelper.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/TransportHelper.cs.meta new file mode 100644 index 0000000..9821f2c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/TransportHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6ced44bfdb3068f4cb7513c9be85729a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/Typed.meta b/Assets/FishNet/CodeGenerating/Helpers/Typed.meta new file mode 100644 index 0000000..f51ce71 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 192c16e1ad7eca84cbcc19073c945ca9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/Typed/Comparers.cs b/Assets/FishNet/CodeGenerating/Helpers/Typed/Comparers.cs new file mode 100644 index 0000000..429a0d4 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed/Comparers.cs @@ -0,0 +1,34 @@ +using MonoFN.Cecil; +using System.Collections.Generic; + +namespace FishNet.CodeGenerating.Helping +{ + internal class TypeDefinitionComparer : IEqualityComparer + { + 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 + { + public bool Equals(TypeReference a, TypeReference b) + { + return a.FullName == b.FullName; + } + + public int GetHashCode(TypeReference obj) + { + return obj.FullName.GetHashCode(); + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/Typed/Comparers.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/Typed/Comparers.cs.meta new file mode 100644 index 0000000..dff182e --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed/Comparers.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2b30600f0fdb27c4fb86c310b08f43b5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/Typed/CreatedSyncType.cs b/Assets/FishNet/CodeGenerating/Helpers/Typed/CreatedSyncType.cs new file mode 100644 index 0000000..c6cf9a0 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed/CreatedSyncType.cs @@ -0,0 +1,49 @@ +using MonoFN.Cecil; + +namespace FishNet.CodeGenerating.Helping +{ + + + + internal class CreatedSyncVar + { + public readonly TypeDefinition VariableTd; + public readonly MethodReference GetValueMr; + public readonly MethodReference SetValueMr; + public readonly MethodReference SetSyncIndexMr; + public readonly MethodReference ConstructorMr; + public readonly GenericInstanceType SyncVarGit; + public MethodReference HookMr; + public CreatedSyncVar(GenericInstanceType syncVarGit, TypeDefinition variableTd, MethodReference getValueMr, MethodReference setValueMr, MethodReference setSyncIndexMr,MethodReference hookMr, MethodReference constructorMr) + { + SyncVarGit = syncVarGit; + VariableTd = variableTd; + GetValueMr = getValueMr; + SetValueMr = setValueMr; + SetSyncIndexMr = setSyncIndexMr; + HookMr = hookMr; + ConstructorMr = constructorMr; + } + } + + + internal class CreatedSyncType + { + public TypeDefinition StubClassTypeDefinition; + public MethodReference GetValueMethodReference; + public MethodReference SetValueMethodReference; + public MethodReference GetPreviousClientValueMethodReference; + public MethodReference ReadMethodReference; + public MethodReference ConstructorMethodReference; + public CreatedSyncType(TypeDefinition stubClassTypeDef, MethodReference getMethodRef, MethodReference setMethodRef, MethodReference getPreviousMethodRef, MethodReference readMethodRef, MethodReference constructorMethodRef) + { + StubClassTypeDefinition = stubClassTypeDef; + GetValueMethodReference = getMethodRef; + SetValueMethodReference = setMethodRef; + GetPreviousClientValueMethodReference = getPreviousMethodRef; + ReadMethodReference = readMethodRef; + ConstructorMethodReference = constructorMethodRef; + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/Typed/CreatedSyncType.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/Typed/CreatedSyncType.cs.meta new file mode 100644 index 0000000..2a0abc7 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed/CreatedSyncType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0cae698c9bc732641892caabf04365dc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/Typed/GeneratorHelper.cs b/Assets/FishNet/CodeGenerating/Helpers/Typed/GeneratorHelper.cs new file mode 100644 index 0000000..778f6c9 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed/GeneratorHelper.cs @@ -0,0 +1,190 @@ +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 + { + /// + /// Gets what objectTypeRef will be serialized as. + /// + 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().GetWriteMethodReference(objectTr) != null) + { + base.LogError($"Writer already exist for {objectTr.FullName}."); + return SerializerType.Invalid; + } + } + else + { + if (base.GetClass().GetReadMethodReference(objectTr) != null) + { + base.LogError($"Reader already exist for {objectTr.FullName}."); + return SerializerType.Invalid; + } + } + + objectTd = objectTr.CachedResolve(base.Session); + //Invalid typeDef. + if (objectTd == null) + { + base.LogError($"{errorPrefix}{objectTd.FullName} could not be resolved."); + return SerializerType.Invalid; + } + //Intentionally excluded. + if (objectTd.CustomAttributes.Count > 0) + { + foreach (CustomAttribute item in objectTd.CustomAttributes) + { + if (item.AttributeType.Is(typeof(CodegenExcludeAttribute))) + return SerializerType.Invalid; + } + } + + //By reference. + if (objectTr.IsByReference) + { + base.LogError($"{errorPrefix}Cannot pass {objectTr.Name} by reference"); + return SerializerType.Invalid; + } + /* Arrays have to be processed first because it's possible for them to meet other conditions + * below and be processed wrong. */ + else if (objectTr.IsArray) + { + if (objectTr.IsMultidimensionalArray()) + { + base.LogError($"{errorPrefix}{objectTr.Name} is an unsupported type. Multidimensional arrays are not supported"); + return SerializerType.Invalid; + } + else + { + return SerializerType.Array; + } + } + //Enum. + else if (objectTd.IsEnum) + { + return SerializerType.Enum; + } + else if (objectTd.Is(typeof(Dictionary<,>))) + { + return SerializerType.Dictionary; + } + else if (objectTd.Is(typeof(List<>))) + { + return SerializerType.List; + } + else if (objectTd.Is(typeof(ListCache<>))) + { + return SerializerType.ListCache; + } + else if (objectTd.InheritsFrom(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; + } + } + + + /// + /// Returns if objectTd can have a serializer generated for it. + /// + 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(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; + } + + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/Typed/GeneratorHelper.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/Typed/GeneratorHelper.cs.meta new file mode 100644 index 0000000..bf4c3fa --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed/GeneratorHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9b1882eac63e3d94aad8f41915bc1ab8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/Typed/QOLAttributeType.cs b/Assets/FishNet/CodeGenerating/Helpers/Typed/QOLAttributeType.cs new file mode 100644 index 0000000..314c3d2 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed/QOLAttributeType.cs @@ -0,0 +1,12 @@ +namespace FishNet.CodeGenerating.Helping +{ + + internal enum QolAttributeType + { + None, + Server, + Client + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/Typed/QOLAttributeType.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/Typed/QOLAttributeType.cs.meta new file mode 100644 index 0000000..f6a473d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed/QOLAttributeType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 357a22940018b8e49976e13272c5b2ef +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/Typed/SerializatierType.cs b/Assets/FishNet/CodeGenerating/Helpers/Typed/SerializatierType.cs new file mode 100644 index 0000000..17b7a7a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed/SerializatierType.cs @@ -0,0 +1,20 @@ +namespace FishNet.CodeGenerating.Helping +{ + + internal enum SerializerType + { + Invalid, + Enum, + Array, + List, + ListCache, + NetworkBehaviour, + ClassOrStruct, + Nullable, + Dictionary, + Null, + ByReference, + MultiDimensionalArray + } + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/Typed/SerializatierType.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/Typed/SerializatierType.cs.meta new file mode 100644 index 0000000..a993edc --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed/SerializatierType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e7f1bbf5c398c3e4e92e53ec3e49d5e9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/Typed/SyncIndexData.cs b/Assets/FishNet/CodeGenerating/Helpers/Typed/SyncIndexData.cs new file mode 100644 index 0000000..ecede30 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed/SyncIndexData.cs @@ -0,0 +1,17 @@ +using MonoFN.Cecil.Cil; +using System.Collections.Generic; + +namespace FishNet.CodeGenerating.Helping +{ + + /// + /// Data used to modify an RpcIndex should the class have to be rebuilt. + /// + internal class SyncIndexData + { + public uint SyncCount = 0; + public List DelegateInstructions = new List(); + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/Typed/SyncIndexData.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/Typed/SyncIndexData.cs.meta new file mode 100644 index 0000000..a2adbd2 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed/SyncIndexData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 55f2e751e4788464b8394f6b8bdb548a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/Typed/SyncType.cs b/Assets/FishNet/CodeGenerating/Helpers/Typed/SyncType.cs new file mode 100644 index 0000000..c5dd521 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed/SyncType.cs @@ -0,0 +1,14 @@ +namespace FishNet.CodeGenerating.Helping +{ + + public enum SyncType + { + Unset, + Variable, + List, + Dictionary, + HashSet, + Custom + } + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/Typed/SyncType.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/Typed/SyncType.cs.meta new file mode 100644 index 0000000..b60f670 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed/SyncType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 44c753d6ac0c7864c9962d91703b2afe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/WriterGenerator.cs b/Assets/FishNet/CodeGenerating/Helpers/WriterGenerator.cs new file mode 100644 index 0000000..c42cb5a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/WriterGenerator.cs @@ -0,0 +1 @@ +//Remove on 2024/01/01 \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/WriterGenerator.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/WriterGenerator.cs.meta new file mode 100644 index 0000000..06dad80 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/WriterGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a4ff3023050c3ee41b59523def204ac8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/WriterHelper.cs b/Assets/FishNet/CodeGenerating/Helpers/WriterHelper.cs new file mode 100644 index 0000000..c42cb5a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/WriterHelper.cs @@ -0,0 +1 @@ +//Remove on 2024/01/01 \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/WriterHelper.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/WriterHelper.cs.meta new file mode 100644 index 0000000..33a47e1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/WriterHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2bbdbcfc675aaff469cadbee89f49c12 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs b/Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs new file mode 100644 index 0000000..5a40872 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs @@ -0,0 +1,100 @@ +using FishNet.Object; +using FishNet.Serializing; +using MonoFN.Cecil; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace FishNet.CodeGenerating.Helping +{ + internal class WriterImports : CodegenBase + { + #region Reflection references. + public MethodReference WriterPool_GetWriter_MethodRef; + public MethodReference WriterPool_GetWriterLength_MethodRef; + public MethodReference Writer_WritePackedWhole_MethodRef; + public TypeReference PooledWriter_TypeRef; + public TypeReference Writer_TypeRef; + public MethodReference PooledWriter_Dispose_MethodRef; + public MethodReference Writer_WriteDictionary_MethodRef; + public MethodReference Writer_WriteList_MethodRef; + public MethodReference Writer_WriteListCache_MethodRef; + public MethodReference Writer_WriteArray_MethodRef; + public TypeReference AutoPackTypeRef; + + public TypeReference GenericWriterTypeRef; + public TypeReference WriterTypeRef; + public MethodReference WriteGetSetMethodRef; + public MethodReference WriteAutoPackGetSetMethodRef; + #endregion + + /// + /// Imports references needed by this helper. + /// + /// + /// + public override bool ImportReferences() + { + PooledWriter_TypeRef = base.ImportReference(typeof(PooledWriter)); + Writer_TypeRef = base.ImportReference(typeof(Writer)); + AutoPackTypeRef = base.ImportReference(typeof(AutoPackType)); + + GenericWriterTypeRef = base.ImportReference(typeof(GenericWriter<>)); + WriterTypeRef = base.ImportReference(typeof(Writer)); + + PropertyInfo writePropertyInfo; + writePropertyInfo = typeof(GenericWriter<>).GetProperty(nameof(GenericWriter.Write)); + WriteGetSetMethodRef = base.ImportReference(writePropertyInfo.GetSetMethod()); + writePropertyInfo = typeof(GenericWriter<>).GetProperty(nameof(GenericWriter.WriteAutoPack)); + WriteAutoPackGetSetMethodRef = base.ImportReference(writePropertyInfo.GetSetMethod()); + + //WriterPool.GetWriter + Type writerPoolType = typeof(WriterPool); + base.ImportReference(writerPoolType); + foreach (var methodInfo in writerPoolType.GetMethods()) + { + if (methodInfo.Name == nameof(WriterPool.GetWriter)) + { + //GetWriter(). + if (methodInfo.GetParameters().Length == 0) + { + WriterPool_GetWriter_MethodRef = base.ImportReference(methodInfo); + } + //GetWriter(?). + else if (methodInfo.GetParameters().Length == 1) + { + ParameterInfo pi = methodInfo.GetParameters()[0]; + //GetWriter(int). + if (pi.ParameterType == typeof(int)) + WriterPool_GetWriterLength_MethodRef = base.ImportReference(methodInfo); + } + } + } + + WriterProcessor gwh = base.GetClass(); + Type pooledWriterType = typeof(PooledWriter); + foreach (MethodInfo methodInfo in pooledWriterType.GetMethods()) + { + int parameterCount = methodInfo.GetParameters().Length; + + if (methodInfo.Name == nameof(PooledWriter.Dispose)) + PooledWriter_Dispose_MethodRef = base.ImportReference(methodInfo); + else if (methodInfo.Name == nameof(PooledWriter.WritePackedWhole)) + Writer_WritePackedWhole_MethodRef = base.ImportReference(methodInfo); + //Relay writers. + else if (parameterCount == 1 && methodInfo.Name == nameof(PooledWriter.WriteDictionary)) + Writer_WriteDictionary_MethodRef = base.ImportReference(methodInfo); + else if (parameterCount == 1 && methodInfo.Name == nameof(PooledWriter.WriteList)) + Writer_WriteList_MethodRef = base.ImportReference(methodInfo); + else if (parameterCount == 1 && methodInfo.Name == nameof(PooledWriter.WriteListCache)) + Writer_WriteListCache_MethodRef = base.ImportReference(methodInfo); + else if (parameterCount == 1 && methodInfo.Name == nameof(PooledWriter.WriteArray)) + Writer_WriteArray_MethodRef = base.ImportReference(methodInfo); + } + + return true; + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs.meta new file mode 100644 index 0000000..43c69fa --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 425638e29bab6f1488e8865c9e3f8b57 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/ILCore.meta b/Assets/FishNet/CodeGenerating/ILCore.meta new file mode 100644 index 0000000..8040bd1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/ILCore.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: abe522a1ad3df3a43a5c3389e3b8ee89 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/ILCore/FishNetILPP.cs b/Assets/FishNet/CodeGenerating/ILCore/FishNetILPP.cs new file mode 100644 index 0000000..46528b1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/ILCore/FishNetILPP.cs @@ -0,0 +1,505 @@ +using FishNet.Broadcast; +using FishNet.CodeGenerating.Extension; +using FishNet.CodeGenerating.Helping; +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.CodeGenerating.Processing; +using FishNet.CodeGenerating.Processing.Rpc; +using FishNet.Configuring; +using FishNet.Serializing.Helping; +using MonoFN.Cecil; +using MonoFN.Cecil.Cil; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Unity.CompilationPipeline.Common.ILPostProcessing; + +namespace FishNet.CodeGenerating.ILCore +{ + public class FishNetILPP : ILPostProcessor + { + #region Const. + internal const string RUNTIME_ASSEMBLY_NAME = "FishNet.Runtime"; + #endregion + + public override bool WillProcess(ICompiledAssembly compiledAssembly) + { + if (compiledAssembly.Name.StartsWith("Unity.")) + return false; + if (compiledAssembly.Name.StartsWith("UnityEngine.")) + return false; + if (compiledAssembly.Name.StartsWith("UnityEditor.")) + return false; + if (compiledAssembly.Name.Contains("Editor")) + return false; + + /* This line contradicts the one below where referencesFishNet + * becomes true if the assembly is FishNetAssembly. This is here + * intentionally to stop codegen from running on the runtime + * fishnet assembly, but the option below is for debugging. I would + * comment out this check if I wanted to compile fishnet runtime. */ + //if (CODEGEN_THIS_NAMESPACE.Length == 0) + //{ + // if (compiledAssembly.Name == RUNTIME_ASSEMBLY_NAME) + // return false; + //} + bool referencesFishNet = FishNetILPP.IsFishNetAssembly(compiledAssembly) || compiledAssembly.References.Any(filePath => Path.GetFileNameWithoutExtension(filePath) == RUNTIME_ASSEMBLY_NAME); + return referencesFishNet; + } + public override ILPostProcessor GetInstance() => this; + + public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) + { + AssemblyDefinition assemblyDef = ILCoreHelper.GetAssemblyDefinition(compiledAssembly); + if (assemblyDef == null) + return null; + + //Check WillProcess again; somehow certain editor scripts skip the WillProcess check. + if (!WillProcess(compiledAssembly)) + return null; + + CodegenSession session = new CodegenSession(); + if (!session.Initialize(assemblyDef.MainModule)) + return null; + + bool modified = false; + + bool fnAssembly = IsFishNetAssembly(compiledAssembly); + if (fnAssembly) + modified |= ModifyMakePublicMethods(session); + /* If one or more scripts use RPCs but don't inherit NetworkBehaviours + * then don't bother processing the rest. */ + if (session.GetClass().NonNetworkBehaviourHasInvalidAttributes(session.Module.Types)) + return new ILPostProcessResult(null, session.Diagnostics); + + modified |= session.GetClass().Process(); + modified |= session.GetClass().Process(); + modified |= CreateDeclaredSerializerDelegates(session); + modified |= CreateDeclaredSerializers(session); + modified |= CreateDeclaredComparerDelegates(session); + modified |= CreateIBroadcast(session); + modified |= CreateQOLAttributes(session); + modified |= CreateNetworkBehaviours(session); + modified |= CreateGenericReadWriteDelegates(session); + + if (fnAssembly) + { + AssemblyNameReference anr = session.Module.AssemblyReferences.FirstOrDefault(x => x.FullName == session.Module.Assembly.FullName); + if (anr != null) + session.Module.AssemblyReferences.Remove(anr); + } + + /* If there are warnings about SyncVars being in different assemblies. + * This is awful ... codegen would need to be reworked to save + * syncvars across all assemblies so that scripts referencing them from + * another assembly can have it's instructions changed. This however is an immense + * amount of work so it will have to be put on hold, for... a long.. long while. */ + if (session.DifferentAssemblySyncVars.Count > 0) + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine($"Assembly {session.Module.Name} has inherited access to SyncVars in different assemblies. When accessing SyncVars across assemblies be sure to use Get/Set methods withinin the inherited assembly script to change SyncVars. Accessible fields are:"); + + foreach (FieldDefinition item in session.DifferentAssemblySyncVars) + sb.AppendLine($"Field {item.Name} within {item.DeclaringType.FullName} in assembly {item.Module.Name}."); + + session.LogWarning("v------- IMPORTANT -------v"); + session.LogWarning(sb.ToString()); + session.DifferentAssemblySyncVars.Clear(); + } + + //session.LogWarning($"Assembly {compiledAssembly.Name} took {stopwatch.ElapsedMilliseconds}."); + if (!modified) + { + return null; + } + else + { + MemoryStream pe = new MemoryStream(); + MemoryStream pdb = new MemoryStream(); + WriterParameters writerParameters = new WriterParameters + { + SymbolWriterProvider = new PortablePdbWriterProvider(), + SymbolStream = pdb, + WriteSymbols = true + }; + assemblyDef.Write(pe, writerParameters); + return new ILPostProcessResult(new InMemoryAssembly(pe.ToArray(), pdb.ToArray()), session.Diagnostics); + } + } + + /// + /// Makees methods public scope which use CodegenMakePublic attribute. + /// + /// + private bool ModifyMakePublicMethods(CodegenSession session) + { + string makePublicTypeFullName = typeof(CodegenMakePublicAttribute).FullName; + foreach (TypeDefinition td in session.Module.Types) + { + foreach (MethodDefinition md in td.Methods) + { + foreach (CustomAttribute ca in md.CustomAttributes) + { + if (ca.AttributeType.FullName == makePublicTypeFullName) + { + md.Attributes &= ~MethodAttributes.Assembly; + md.Attributes |= MethodAttributes.Public; + } + } + } + } + + //There is always at least one modified. + return true; + } + /// + /// Creates delegates for user declared serializers. + /// + internal bool CreateDeclaredSerializerDelegates(CodegenSession session) + { + bool modified = false; + + TypeAttributes readWriteExtensionTypeAttr = (TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Abstract); + List allTypeDefs = session.Module.Types.ToList(); + foreach (TypeDefinition td in allTypeDefs) + { + if (session.GetClass().IgnoreTypeDefinition(td)) + continue; + + if (td.Attributes.HasFlag(readWriteExtensionTypeAttr)) + modified |= session.GetClass().CreateSerializerDelegates(td, true); + } + + return modified; + } + + /// + /// Creates serializers for custom types within user declared serializers. + /// + private bool CreateDeclaredSerializers(CodegenSession session) + { + bool modified = false; + + TypeAttributes readWriteExtensionTypeAttr = (TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Abstract); + List allTypeDefs = session.Module.Types.ToList(); + foreach (TypeDefinition td in allTypeDefs) + { + if (session.GetClass().IgnoreTypeDefinition(td)) + continue; + + if (td.Attributes.HasFlag(readWriteExtensionTypeAttr)) + modified |= session.GetClass().CreateSerializers(td); + } + + return modified; + } + + /// + /// Creates delegates for user declared comparers. + /// + internal bool CreateDeclaredComparerDelegates(CodegenSession session) + { + bool modified = false; + List allTypeDefs = session.Module.Types.ToList(); + foreach (TypeDefinition td in allTypeDefs) + { + if (session.GetClass().IgnoreTypeDefinition(td)) + continue; + + modified |= session.GetClass().CreateComparerDelegates(td); + } + + return modified; + } + + + /// + /// Creaters serializers and calls for IBroadcast. + /// + /// + /// + private bool CreateIBroadcast(CodegenSession session) + { + bool modified = false; + + string networkBehaviourFullName = session.GetClass().FullName; + HashSet typeDefs = new HashSet(); + 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()) + typeDefs.Add(climbTd); + //7ms + + //Add nested. Only going to go a single layer deep. + foreach (TypeDefinition nestedTypeDef in td.NestedTypes) + { + if (nestedTypeDef.ImplementsInterface()) + 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().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; + } + + /// + /// Handles QOLAttributes such as [Server]. + /// + /// + private bool CreateQOLAttributes(CodegenSession session) + { + bool modified = false; + + bool codeStripping = false; + + List 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().IgnoreTypeDefinition(td)) + continue; + + modified |= session.GetClass().Process(td, codeStripping); + } + + + + return modified; + } + + /// + /// Creates NetworkBehaviour changes. + /// + /// + /// + private bool CreateNetworkBehaviours(CodegenSession session) + { + bool modified = false; + //Get all network behaviours to process. + List networkBehaviourTypeDefs = session.Module.Types + .Where(td => td.IsSubclassOf(session, session.GetClass().FullName)) + .ToList(); + + //Moment a NetworkBehaviour exist the assembly is considered modified. + if (networkBehaviourTypeDefs.Count > 0) + modified = true; + + /* Remove types which are inherited. This gets the child most networkbehaviours. + * Since processing iterates all parent classes there's no reason to include them */ + RemoveInheritedTypeDefinitions(networkBehaviourTypeDefs); + //Set how many rpcs are in children classes for each typedef. + Dictionary inheritedRpcCounts = new Dictionary(); + SetChildRpcCounts(inheritedRpcCounts, networkBehaviourTypeDefs); + //Set how many synctypes are in children classes for each typedef. + Dictionary inheritedSyncTypeCounts = new Dictionary(); + SetChildSyncTypeCounts(inheritedSyncTypeCounts, networkBehaviourTypeDefs); + + /* This holds all sync types created, synclist, dictionary, var + * and so on. This data is used after all syncvars are made so + * other methods can look for references to created synctypes and + * replace accessors accordingly. */ + List<(SyncType, ProcessedSync)> allProcessedSyncs = new List<(SyncType, ProcessedSync)>(); + HashSet allProcessedCallbacks = new HashSet(); + List processedClasses = new List(); + + foreach (TypeDefinition typeDef in networkBehaviourTypeDefs) + { + session.ImportReference(typeDef); + //Synctypes processed for this nb and it's inherited classes. + List<(SyncType, ProcessedSync)> processedSyncs = new List<(SyncType, ProcessedSync)>(); + session.GetClass().Process(typeDef, processedSyncs, + inheritedSyncTypeCounts, inheritedRpcCounts); + //Add to all processed. + allProcessedSyncs.AddRange(processedSyncs); + } + + /* Must run through all scripts should user change syncvar + * from outside the networkbehaviour. */ + if (allProcessedSyncs.Count > 0) + { + foreach (TypeDefinition td in session.Module.Types) + { + session.GetClass().ReplaceGetSets(td, allProcessedSyncs); + session.GetClass().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 tds) + { + HashSet inheritedTds = new HashSet(); + /* Remove any networkbehaviour typedefs which are inherited by + * another networkbehaviour typedef. When a networkbehaviour typedef + * is processed so are all of the inherited types. */ + for (int i = 0; i < tds.Count; i++) + { + /* Iterates all base types and + * adds them to inheritedTds so long + * as the base type is not a NetworkBehaviour. */ + TypeDefinition copyTd = tds[i].GetNextBaseTypeDefinition(session); + while (copyTd != null) + { + //Class is NB. + if (copyTd.FullName == session.GetClass().FullName) + break; + + inheritedTds.Add(copyTd); + copyTd = copyTd.GetNextBaseTypeDefinition(session); + } + } + + //Remove all inherited types. + foreach (TypeDefinition item in inheritedTds) + tds.Remove(item); + } + + /* Sets how many Rpcs are within the children + * of each typedefinition. EG: if our structure is + * A : B : C, with the following RPC counts... + * A 3 + * B 1 + * C 2 + * then B child rpc counts will be 3, and C will be 4. */ + void SetChildRpcCounts(Dictionary typeDefCounts, List tds) + { + foreach (TypeDefinition typeDef in tds) + { + //Number of RPCs found while climbing typeDef. + uint childCount = 0; + + TypeDefinition copyTd = typeDef; + do + { + //How many RPCs are in copyTd. + uint copyCount = session.GetClass().GetRpcCount(copyTd); + + /* If not found it this is the first time being + * processed. When this occurs set the value + * to 0. It will be overwritten below if baseCount + * is higher. */ + uint previousCopyChildCount = 0; + if (!typeDefCounts.TryGetValue(copyTd, out previousCopyChildCount)) + typeDefCounts[copyTd] = 0; + /* If baseCount is higher then replace count for copyTd. + * This can occur when a class is inherited by several types + * and the first processed type might only have 1 rpc, while + * the next has 2. This could be better optimized but to keep + * the code easier to read, it will stay like this. */ + if (childCount > previousCopyChildCount) + typeDefCounts[copyTd] = childCount; + + //Increase baseCount with RPCs found here. + childCount += copyCount; + + copyTd = copyTd.GetNextBaseClassToProcess(session); + } while (copyTd != null); + } + + } + + + /* This performs the same functionality as SetChildRpcCounts + * but for SyncTypes. */ + void SetChildSyncTypeCounts(Dictionary typeDefCounts, List tds) + { + foreach (TypeDefinition typeDef in tds) + { + //Number of RPCs found while climbing typeDef. + uint childCount = 0; + + TypeDefinition copyTd = typeDef; + /* Iterate up to the parent script and then reverse + * the order. This is so that the topmost is 0 + * and each inerhiting script adds onto that. + * Setting child types this way makes it so parent + * types don't need to have their synctype/rpc counts + * rebuilt when scripts are later to be found + * inheriting from them. */ + List reversedTypeDefs = new List(); + do + { + reversedTypeDefs.Add(copyTd); + copyTd = copyTd.GetNextBaseClassToProcess(session); + } while (copyTd != null); + reversedTypeDefs.Reverse(); + + foreach (TypeDefinition td in reversedTypeDefs) + { + //How many RPCs are in copyTd. + uint copyCount = session.GetClass().GetSyncTypeCount(td); + /* If not found it this is the first time being + * processed. When this occurs set the value + * to 0. It will be overwritten below if baseCount + * is higher. */ + uint previousCopyChildCount = 0; + if (!typeDefCounts.TryGetValue(td, out previousCopyChildCount)) + typeDefCounts[td] = 0; + /* If baseCount is higher then replace count for copyTd. + * This can occur when a class is inherited by several types + * and the first processed type might only have 1 rpc, while + * the next has 2. This could be better optimized but to keep + * the code easier to read, it will stay like this. */ + if (childCount > previousCopyChildCount) + typeDefCounts[td] = childCount; + //Increase baseCount with RPCs found here. + childCount += copyCount; + } + } + } + + + return modified; + } + + /// + /// Creates generic delegates for all read and write methods. + /// + /// + /// + private bool CreateGenericReadWriteDelegates(CodegenSession session) + { + session.GetClass().CreateStaticMethodDelegates(); + session.GetClass().CreateStaticMethodDelegates(); + + return true; + } + + internal static bool IsFishNetAssembly(ICompiledAssembly assembly) => (assembly.Name == FishNetILPP.RUNTIME_ASSEMBLY_NAME); + internal static bool IsFishNetAssembly(CodegenSession session) => (session.Module.Assembly.Name.Name == FishNetILPP.RUNTIME_ASSEMBLY_NAME); + internal static bool IsFishNetAssembly(ModuleDefinition moduleDef) => (moduleDef.Assembly.Name.Name == FishNetILPP.RUNTIME_ASSEMBLY_NAME); + + } +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/ILCore/FishNetILPP.cs.meta b/Assets/FishNet/CodeGenerating/ILCore/FishNetILPP.cs.meta new file mode 100644 index 0000000..38eb26f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/ILCore/FishNetILPP.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f03d76b376c1d5b4591039af6fd4c9e0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/ILCore/ILCoreHelper.cs b/Assets/FishNet/CodeGenerating/ILCore/ILCoreHelper.cs new file mode 100644 index 0000000..054dc56 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/ILCore/ILCoreHelper.cs @@ -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 + { + + /// + /// Returns AssembleDefinition for compiledAssembly. + /// + /// + /// + internal static AssemblyDefinition GetAssemblyDefinition(ICompiledAssembly compiledAssembly) + { + PostProcessorAssemblyResolver assemblyResolver = new PostProcessorAssemblyResolver(compiledAssembly); + ReaderParameters readerParameters = new ReaderParameters + { + SymbolStream = new MemoryStream(compiledAssembly.InMemoryAssembly.PdbData), + SymbolReaderProvider = new PortablePdbReaderProvider(), + AssemblyResolver = assemblyResolver, + ReflectionImporterProvider = new PostProcessorReflectionImporterProvider(), + ReadingMode = ReadingMode.Immediate + }; + + AssemblyDefinition assemblyDefinition = AssemblyDefinition.ReadAssembly(new MemoryStream(compiledAssembly.InMemoryAssembly.PeData), readerParameters); + //Allows us to resolve inside FishNet assembly, such as for components. + assemblyResolver.AddAssemblyDefinitionBeingOperatedOn(assemblyDefinition); + + return assemblyDefinition; + } + + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/ILCore/ILCoreHelper.cs.meta b/Assets/FishNet/CodeGenerating/ILCore/ILCoreHelper.cs.meta new file mode 100644 index 0000000..63a163d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/ILCore/ILCoreHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dfcfb917dd9268744962ae61aa0115b7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/ILCore/PostProcessorAssemblyResolver.cs b/Assets/FishNet/CodeGenerating/ILCore/PostProcessorAssemblyResolver.cs new file mode 100644 index 0000000..6a9ca63 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/ILCore/PostProcessorAssemblyResolver.cs @@ -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 m_AssemblyCache = new Dictionary(); + private readonly ICompiledAssembly m_CompiledAssembly; + private AssemblyDefinition m_SelfAssembly; + + public PostProcessorAssemblyResolver(ICompiledAssembly compiledAssembly) + { + m_CompiledAssembly = compiledAssembly; + m_AssemblyReferences = compiledAssembly.References; + } + + public void Dispose() { } + + public AssemblyDefinition Resolve(AssemblyNameReference name) => Resolve(name, new ReaderParameters(ReadingMode.Deferred)); + + public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters) + { + lock (m_AssemblyCache) + { + if (name.Name == m_CompiledAssembly.Name) + { + return m_SelfAssembly; + } + + var fileName = FindFile(name); + if (fileName == null) + { + return null; + } + + var lastWriteTime = File.GetLastWriteTime(fileName); + var cacheKey = $"{fileName}{lastWriteTime}"; + if (m_AssemblyCache.TryGetValue(cacheKey, out var result)) + { + return result; + } + + parameters.AssemblyResolver = this; + + var ms = MemoryStreamFor(fileName); + var pdb = $"{fileName}.pdb"; + if (File.Exists(pdb)) + { + parameters.SymbolStream = MemoryStreamFor(pdb); + } + + var assemblyDefinition = AssemblyDefinition.ReadAssembly(ms, parameters); + m_AssemblyCache.Add(cacheKey, assemblyDefinition); + + return assemblyDefinition; + } + } + + private string FindFile(AssemblyNameReference name) + { + var fileName = m_AssemblyReferences.FirstOrDefault(r => Path.GetFileName(r) == $"{name.Name}.dll"); + if (fileName != null) + { + return fileName; + } + + // perhaps the type comes from an exe instead + fileName = m_AssemblyReferences.FirstOrDefault(r => Path.GetFileName(r) == $"{name.Name}.exe"); + if (fileName != null) + { + return fileName; + } + + //Unfortunately the current ICompiledAssembly API only provides direct references. + //It is very much possible that a postprocessor ends up investigating a type in a directly + //referenced assembly, that contains a field that is not in a directly referenced assembly. + //if we don't do anything special for that situation, it will fail to resolve. We should fix this + //in the ILPostProcessing API. As a workaround, we rely on the fact here that the indirect references + //are always located next to direct references, so we search in all directories of direct references we + //got passed, and if we find the file in there, we resolve to it. + return m_AssemblyReferences + .Select(Path.GetDirectoryName) + .Distinct() + .Select(parentDir => Path.Combine(parentDir, $"{name.Name}.dll")) + .FirstOrDefault(File.Exists); + } + + private static MemoryStream MemoryStreamFor(string fileName) + { + return Retry(10, TimeSpan.FromSeconds(1), () => + { + byte[] byteArray; + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + byteArray = new byte[fs.Length]; + var readLength = fs.Read(byteArray, 0, (int)fs.Length); + if (readLength != fs.Length) + { + throw new InvalidOperationException("File read length is not full length of file."); + } + } + + return new MemoryStream(byteArray); + }); + } + + private static MemoryStream Retry(int retryCount, TimeSpan waitTime, Func 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; + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/ILCore/PostProcessorAssemblyResolver.cs.meta b/Assets/FishNet/CodeGenerating/ILCore/PostProcessorAssemblyResolver.cs.meta new file mode 100644 index 0000000..1a05af5 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/ILCore/PostProcessorAssemblyResolver.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2c247f4266b2864eb96e6a9ae6557d31 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporter.cs b/Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporter.cs new file mode 100644 index 0000000..32b9e8c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporter.cs @@ -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); + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporter.cs.meta b/Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporter.cs.meta new file mode 100644 index 0000000..8dca5f1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 484e8ad8c4dde382ea67036b32935ef1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporterProvider.cs b/Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporterProvider.cs new file mode 100644 index 0000000..7d38602 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporterProvider.cs @@ -0,0 +1,12 @@ +using MonoFN.Cecil; + +namespace FishNet.CodeGenerating.ILCore +{ + internal class PostProcessorReflectionImporterProvider : IReflectionImporterProvider + { + public IReflectionImporter GetReflectionImporter(ModuleDefinition moduleDef) + { + return new PostProcessorReflectionImporter(moduleDef); + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporterProvider.cs.meta b/Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporterProvider.cs.meta new file mode 100644 index 0000000..12a58b8 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporterProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f9273a5dad109ab0783891e36c983080 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Processing.meta b/Assets/FishNet/CodeGenerating/Processing.meta new file mode 100644 index 0000000..21c7e9a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b0d1eb51001374741a4c4de01c3bc05d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Processing/CodegenBase.cs b/Assets/FishNet/CodeGenerating/Processing/CodegenBase.cs new file mode 100644 index 0000000..9634513 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/CodegenBase.cs @@ -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; + } + + /// + /// Returns class of type if found within Session. + /// + /// + /// + internal T GetClass() where T : CodegenBase => Session.GetClass(); + + #region Logging. + /// + /// Logs a warning. + /// + /// + internal void LogWarning(string msg) => Session.LogWarning(msg); + /// + /// Logs an error. + /// + /// + 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 + + } + +} diff --git a/Assets/FishNet/CodeGenerating/Processing/CodegenBase.cs.meta b/Assets/FishNet/CodeGenerating/Processing/CodegenBase.cs.meta new file mode 100644 index 0000000..90b497e --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/CodegenBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8462034e5255191499a018bd8fbbf751 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Processing/CustomSerializerProcessor.cs b/Assets/FishNet/CodeGenerating/Processing/CustomSerializerProcessor.cs new file mode 100644 index 0000000..038cc61 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/CustomSerializerProcessor.cs @@ -0,0 +1,359 @@ + +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().CodegenExclude(methodDef)) + continue; + + MethodReference methodRef = base.ImportReference(methodDef); + if (extensionType == ExtensionType.Write) + { + base.GetClass().AddWriterMethod(methodRef.Parameters[1].ParameterType, methodRef, false, !replace); + modified = true; + } + else if (extensionType == ExtensionType.Read) + { + base.GetClass().AddReaderMethod(methodRef.ReturnType, methodRef, false, !replace); + modified = true; + } + } + + return modified; + } + + /// + /// Creates serializers for any custom types for declared methods. + /// + /// + /// + internal bool CreateSerializers(TypeDefinition typeDef) + { + bool modified = false; + + List<(MethodDefinition, ExtensionType)> declaredMethods = new List<(MethodDefinition, ExtensionType)>(); + /* Go through all custom serializers again and see if + * they use any types that the user didn't make a serializer for + * and that there isn't a built-in type for. Create serializers + * for these types. */ + foreach (MethodDefinition methodDef in typeDef.Methods) + { + ExtensionType extensionType = GetExtensionType(methodDef); + if (extensionType == ExtensionType.None) + continue; + if (base.GetClass().CodegenExclude(methodDef)) + continue; + + declaredMethods.Add((methodDef, extensionType)); + modified = true; + } + //Now that all declared are loaded see if any of them need generated serializers. + foreach ((MethodDefinition methodDef, ExtensionType extensionType) in declaredMethods) + CreateSerializers(extensionType, methodDef); + + return modified; + } + + + /// + /// Creates a custom serializer for any types not handled within users declared. + /// + /// + /// + /// + /// + private void CreateSerializers(ExtensionType extensionType, MethodDefinition methodDef) + { + for (int i = 0; i < methodDef.Body.Instructions.Count; i++) + CheckToModifyInstructions(extensionType, methodDef, ref i); + } + + /// + /// Creates delegates for custom comparers. + /// + internal bool CreateComparerDelegates(TypeDefinition typeDef) + { + bool modified = false; + GeneralHelper gh = base.GetClass(); + /* Find all declared methods and register delegates to them. + * After they are all registered create any custom writers + * needed to complete the declared methods. It's important to + * make generated writers after so that a generated method + * isn't made for a type when the user has already made a declared one. */ + foreach (MethodDefinition methodDef in typeDef.Methods) + { + if (gh.CodegenExclude(methodDef)) + continue; + if (!methodDef.HasCustomAttribute()) + 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; + } + + + /// + /// Checks if instructions need to be modified and does so. + /// + /// + /// + 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); + } + + + /// + /// Checks if a reader or writer must be generated for a field type. + /// + /// + /// + 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); + } + } + + + /// + /// Checks if a reader or writer must be generated for a call type. + /// + /// + /// + /// + /// + /// + 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(nameof(Writer.Write)) || + method.Is(nameof(Reader.Read)) + ); + + if (canCreate) + { + GenericInstanceMethod instanceMethod = (GenericInstanceMethod)method; + TypeReference parameterType = instanceMethod.GenericArguments[0]; + if (parameterType.IsGenericParameter) + return; + + CreateReaderOrWriter(extensionType, methodDef, ref instructionIndex, parameterType); + } + } + + + /// + /// Creates a reader or writer for parameterType if needed. Otherwise calls existing reader. + /// + private void CreateReaderOrWriter(ExtensionType extensionType, MethodDefinition methodDef, ref int instructionIndex, TypeReference parameterType) + { + if (!parameterType.IsGenericParameter && parameterType.CanBeResolved(base.Session)) + { + TypeDefinition typeDefinition = parameterType.CachedResolve(base.Session); + //If class and not value type check for accessible constructor. + if (typeDefinition.IsClass && !typeDefinition.IsValueType) + { + MethodDefinition constructor = typeDefinition.GetMethod(".ctor"); + //Constructor is inaccessible, cannot create serializer for type. + if (!constructor.IsPublic) + { + base.LogError($"Unable to generator serializers for {typeDefinition.FullName} because it's constructor is not public."); + return; + } + } + + ILProcessor processor = methodDef.Body.GetILProcessor(); + + //Find already existing read or write method. + MethodReference createdMethodRef = (extensionType == ExtensionType.Write) ? + base.GetClass().GetWriteMethodReference(parameterType) : + base.GetClass().GetReadMethodReference(parameterType); + //If a created method already exist nothing further is required. + if (createdMethodRef != null) + { + TryInsertAutoPack(ref instructionIndex); + //Replace call to generic with already made serializer. + Instruction newInstruction = processor.Create(OpCodes.Call, createdMethodRef); + methodDef.Body.Instructions[instructionIndex] = newInstruction; + return; + } + else + { + createdMethodRef = (extensionType == ExtensionType.Write) ? + base.GetClass().CreateWriter(parameterType) : + base.GetClass().CreateReader(parameterType); + } + + //If method was created. + if (createdMethodRef != null) + { + TryInsertAutoPack(ref instructionIndex); + //Set new instruction. + Instruction newInstruction = processor.Create(OpCodes.Call, createdMethodRef); + methodDef.Body.Instructions[instructionIndex] = newInstruction; + } + } + + void TryInsertAutoPack(ref int insertIndex) + { + if (IsAutoPackMethod(parameterType, extensionType)) + { + ILProcessor processor = methodDef.Body.GetILProcessor(); + AutoPackType packType = base.GetClass().GetDefaultAutoPackType(parameterType); + Instruction autoPack = processor.Create(OpCodes.Ldc_I4, (int)packType); + methodDef.Body.Instructions.Insert(insertIndex, autoPack); + insertIndex++; + } + } + } + + /// + /// Returns if a typeRef serializer requires or uses autopacktype. + /// + private bool IsAutoPackMethod(TypeReference typeRef, ExtensionType extensionType) + { + if (extensionType == ExtensionType.Write) + return base.GetClass().IsAutoPackedType(typeRef); + else if (extensionType == ExtensionType.Read) + return base.GetClass().IsAutoPackedType(typeRef); + else + return false; + + } + /// + /// Returns the RPC attribute on a method, if one exist. Otherwise returns null. + /// + /// + /// + private ExtensionType GetExtensionType(MethodDefinition methodDef) + { + bool hasExtensionAttribute = methodDef.HasCustomAttribute(); + if (!hasExtensionAttribute) + return ExtensionType.None; + + bool write = (methodDef.ReturnType == methodDef.Module.TypeSystem.Void); + + //Return None for Mirror types. +#if MIRROR + if (write) + { + if (methodDef.Parameters.Count > 0 && methodDef.Parameters[0].ParameterType.FullName == "Mirror.NetworkWriter") + return ExtensionType.None; + } + else + { + if (methodDef.Parameters.Count > 0 && methodDef.Parameters[0].ParameterType.FullName == "Mirror.NetworkReader") + return ExtensionType.None; + } +#endif + + + string prefix = (write) ? WriterProcessor.WRITE_PREFIX : ReaderProcessor.READ_PREFIX; + + //Does not contain prefix. + if (methodDef.Name.Length < prefix.Length || methodDef.Name.Substring(0, prefix.Length) != prefix) + return ExtensionType.None; + + //Make sure first parameter is right. + if (methodDef.Parameters.Count >= 1) + { + TypeReference tr = methodDef.Parameters[0].ParameterType; + if (tr.FullName != base.GetClass().Writer_TypeRef.FullName && + tr.FullName != base.GetClass().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; + } + + + } +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Processing/CustomSerializerProcessor.cs.meta b/Assets/FishNet/CodeGenerating/Processing/CustomSerializerProcessor.cs.meta new file mode 100644 index 0000000..8235bda --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/CustomSerializerProcessor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9269fd8a62199e24c965b4c99b641244 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourProcessor.cs b/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourProcessor.cs new file mode 100644 index 0000000..88f9df6 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourProcessor.cs @@ -0,0 +1,637 @@ +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 Types. + private class NetworkInitializeMethodData + { + public MethodDefinition MethodDefinition; + public FieldDefinition CalledFieldDef; + public bool CalledFromAwake; + + public NetworkInitializeMethodData(MethodDefinition methodDefinition, FieldDefinition calledFieldDef) + { + MethodDefinition = methodDefinition; + CalledFieldDef = calledFieldDef; + CalledFromAwake = false; + } + } + private class AwakeMethodData + { + public MethodDefinition AwakeMethodDef; + public MethodDefinition UserLogicMethodDef; + public bool Created; + + public AwakeMethodData(MethodDefinition awakeMd, MethodDefinition userLogicMd, bool created) + { + AwakeMethodDef = awakeMd; + UserLogicMethodDef = userLogicMd; + Created = created; + } + } + #endregion + + #region Misc. + private Dictionary _earlyNetworkInitializeDatas = new Dictionary(); + private Dictionary _lateNetworkInitializeDatas = new Dictionary(); + /// + /// Methods modified or iterated during weaving. + /// + internal List ModifiedMethodDefinitions = new List(); + /// + /// Classes which have been processed for all NetworkBehaviour features. + /// + private HashSet _processedClasses = new HashSet(); + #endregion + + #region Const. + internal const string EARLY_INITIALIZED_NAME = "NetworkInitializeEarly_"; + internal const string LATE_INITIALIZED_NAME = "NetworkInitializeLate_"; + internal const string NETWORKINITIALIZE_EARLY_INTERNAL_NAME = "NetworkInitialize___Early"; + internal const string NETWORKINITIALIZE_LATE_INTERNAL_NAME = "NetworkInitialize__Late"; + private MethodAttributes PUBLIC_VIRTUAL_ATTRIBUTES = (MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig); +#pragma warning disable CS0414 + private MethodAttributes PROTECTED_VIRTUAL_ATTRIBUTES = (MethodAttributes.Family | MethodAttributes.Virtual | MethodAttributes.HideBySig); +#pragma warning restore CS0414 + #endregion + + internal bool Process(TypeDefinition typeDef, List<(SyncType, ProcessedSync)> allProcessedSyncs, Dictionary childSyncTypeCounts, Dictionary childRpcCounts) + { + bool modified = false; + TypeDefinition copyTypeDef = typeDef; + TypeDefinition firstTypeDef = typeDef; + + //Make collection of NBs to processor. + List typeDefs = new List(); + do + { + typeDefs.Add(copyTypeDef); + copyTypeDef = TypeDefinitionExtensionsOld.GetNextBaseClassToProcess(copyTypeDef, base.Session); + } while (copyTypeDef != null); + + /* Iterate from child-most to parent first + * while creating network initialize methods. + * This is because the child-most must call the parents + * base awake methods. */ + foreach (TypeDefinition td in typeDefs) + { + /* Class was already processed. Since child most is processed first + * this can occur if a class is inherited by multiple types. If a class + * has already been processed then there is no reason to scale up the hierarchy + * because it would have already been done. */ + if (HasClassBeenProcessed(td)) + continue; + + //Disallow nested network behaviours. + ICollection nestedTds = td.NestedTypes; + foreach (TypeDefinition item in nestedTds) + { + if (item.InheritsNetworkBehaviour(base.Session)) + { + base.LogError($"{td.FullName} contains nested NetworkBehaviours. These are not supported."); + return modified; + } + } + + /* Create NetworkInitialize before-hand so the other procesors + * can use it. */ + MethodDefinition networkInitializeIfDisabledMd; + CreateNetworkInitializeMethods(td, out networkInitializeIfDisabledMd); + CallNetworkInitializeMethods(networkInitializeIfDisabledMd); + } + + /* Reverse and do RPCs/SyncTypes. + * This counts up on children instead of the + * parent, so we do not have to rewrite + * parent numbers. */ + typeDefs.Reverse(); + + foreach (TypeDefinition td in typeDefs) + { + /* Class was already processed. Since child most is processed first + * this can occur if a class is inherited by multiple types. If a class + * has already been processed then there is no reason to scale up the hierarchy + * because it would have already been done. */ + if (HasClassBeenProcessed(td)) + continue; + + //No longer used...remove in rework. + uint rpcCount = 0; + childRpcCounts.TryGetValue(td, out rpcCount); + /* 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. */ + modified |= base.GetClass().Process(td, ref rpcCount); + //25ms + + /* RPCs. */ + modified |= base.GetClass().ProcessLocal(td, ref rpcCount); + //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. */ + uint syncTypeStartCount; + childSyncTypeCounts.TryGetValue(td, out syncTypeStartCount); + modified |= base.GetClass().Process(td, allProcessedSyncs, ref syncTypeStartCount); + //70ms + _processedClasses.Add(td); + } + + int maxAllowSyncTypes = 256; + if (allProcessedSyncs.Count > maxAllowSyncTypes) + { + base.LogError($"Found {allProcessedSyncs.Count} SyncTypes within {firstTypeDef.FullName}. The maximum number of allowed SyncTypes within type and inherited types is {maxAllowSyncTypes}. Remove SyncTypes or condense them using data containers, or a custom SyncObject."); + return false; + } + + /* If here then all inerited classes for firstTypeDef have + * been processed. */ + PrepareNetworkInitializeMethods(firstTypeDef); + + /* Make awake methods for all inherited classes + * public and virtual. This is so I can add logic + * to the firstTypeDef awake and still execute + * user awake methods. */ + List awakeDatas = new List(); + if (!CreateOrModifyAwakeMethods(firstTypeDef, ref awakeDatas)) + { + base.LogError($"Was unable to make Awake methods public virtual starting on type {firstTypeDef.FullName}."); + return modified; + } + + //NetworkInitializeEarly. + CallNetworkInitializeFromAwake(awakeDatas, true); + //Call base awake, then call user logic methods. + CallBaseAwakeOnCreatedMethods(awakeDatas); + CallAwakeUserLogic(awakeDatas); + //NetworkInitializeLate + CallNetworkInitializeFromAwake(awakeDatas, false); + //Since awake methods are erased ret has to be added at the end. + AddReturnsToAwake(awakeDatas); + + base.GetClass().CallBaseReadSyncVar(firstTypeDef); + + return modified; + } + + + /// + /// Returns if a class has been processed. + /// + /// + /// + private bool HasClassBeenProcessed(TypeDefinition typeDef) + { + return _processedClasses.Contains(typeDef); + } + + /// + /// Returns if any typeDefs have attributes which are not allowed to be used outside NetworkBehaviour. + /// + /// + /// + internal bool NonNetworkBehaviourHasInvalidAttributes(Collection typeDefs) + { + bool error = false; + 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 (base.GetClass().Attributes.HasRpcAttributes(md)) + { + base.LogError($"{typeDef.FullName} has one or more RPC attributes but does not inherit from NetworkBehaviour."); + error = true; + } + } + //Check fields for attribute. + foreach (FieldDefinition fd in typeDef.Fields) + { + if (base.GetClass().GetSyncType(fd, false, out _) != SyncType.Unset) + { + base.LogError($"{typeDef.FullName} has one or more SyncType attributes but does not inherit from NetworkBehaviour."); + error = true; + } + } + } + + return error; + } + + + + /// + /// Calls the next awake method if the nested awake was created by codegen. + /// + /// + private void CallBaseAwakeOnCreatedMethods(List datas) + { + /* Method definitions are added from child most + * so they will always be going up the hierarchy. */ + for (int i = 0; i < datas.Count; i++) + { + AwakeMethodData amd = datas[i]; + /* If the awake already existed + * then let the user code be the final say + * if base is called. */ + if (!amd.Created) + continue; + + TypeDefinition typeDef = amd.AwakeMethodDef.DeclaringType; + + /* Awake will always exist because it was added previously. + * Get awake for the current declaring type. */ + MethodDefinition awakeMd = typeDef.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME); + + MethodReference baseAwakeMr = typeDef.GetMethodReferenceInBase(base.Session, NetworkBehaviourHelper.AWAKE_METHOD_NAME); + if (baseAwakeMr == null) + return; + MethodDefinition baseAwakeMd = baseAwakeMr.CachedResolve(base.Session); + //MethodDefinition baseAwakeMd = typeDef.GetMethodDefinitionInBase(base.Session, NetworkBehaviourHelper.AWAKE_METHOD_NAME); + if (baseAwakeMd == null) + return; + + //Check if they already call base. + ILProcessor processor = awakeMd.Body.GetILProcessor(); + bool alreadyHasBaseCall = false; + //Check if already calls baseAwake. + foreach (var item in awakeMd.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 == baseAwakeMd) + { + alreadyHasBaseCall = true; + break; + } + } + } + } + + if (!alreadyHasBaseCall) + { + //Create instructions for base call. + processor.Emit(OpCodes.Ldarg_0); //base. + processor.Emit(OpCodes.Call, baseAwakeMr); + } + } + } + + + /// + /// Calls the next awake method if the nested awake was created by codegen. + /// + /// + private void CallAwakeUserLogic(List datas) + { + /* Method definitions are added from child most + * so they will always be going up the hierarchy. */ + for (int i = 0; i < datas.Count; i++) + { + AwakeMethodData amd = datas[i]; + //If was created then there is no user logic. + if (amd.Created) + continue; + //If logic method is null. Should never be the case. + if (amd.UserLogicMethodDef == null) + continue; + + MethodDefinition awakeMd = amd.AwakeMethodDef; + base.GetClass().CallCopiedMethod(awakeMd, amd.UserLogicMethodDef); + } + } + + + /// + /// Adds a check to NetworkInitialize to see if it has already run. + /// + /// + private void AddNetworkInitializeExecutedCheck(TypeDefinition firstTypeDef, bool initializeEarly) + { + TypeDefinition copyTypeDef = firstTypeDef; + AddCheck(copyTypeDef, initializeEarly); + + void AddCheck(TypeDefinition td, bool early) + { + string methodName; + string fieldName; + if (early) + { + methodName = NETWORKINITIALIZE_EARLY_INTERNAL_NAME; + fieldName = $"{EARLY_INITIALIZED_NAME}{td.FullName}_{td.Module.Name}"; + } + else + { + methodName = NETWORKINITIALIZE_LATE_INTERNAL_NAME; + fieldName = $"{LATE_INITIALIZED_NAME}{td.FullName}_{td.Module.Name}"; + } + + MethodDefinition md = td.GetMethod(methodName); + if (md == null) + return; + + TypeReference boolTr = base.GetClass().GetTypeReference(typeof(bool)); + FieldReference fr = copyTypeDef.GetOrCreateFieldReference(base.Session, fieldName, FieldAttributes.Private, boolTr, out bool created); + + if (created) + { + List insts = new List(); + 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); + } + } + } + /// + /// Gets the top-most parent away method. + /// + /// + /// + private void PrepareNetworkInitializeMethods(TypeDefinition firstTypeDef) + { + TypeDefinition thisTypeDef = firstTypeDef; + + string[] initializeMethodNames = new string[] { NETWORKINITIALIZE_EARLY_INTERNAL_NAME, NETWORKINITIALIZE_LATE_INTERNAL_NAME }; + + do + { + bool canCallBase = thisTypeDef.CanProcessBaseType(base.Session); + + foreach (string mdName in initializeMethodNames) + { + /* There are no more base calls to make but we still + * need to check if the initialize methods have already ran, so do that + * here. */ + if (canCallBase) + { + /* Awake will always exist because it was added previously. + * Get awake for copy and base of copy. */ + MethodDefinition thisMd = thisTypeDef.GetMethod(mdName); + MethodReference baseMr = thisTypeDef.GetMethodReferenceInBase(base.Session, mdName); + MethodDefinition baseMd = baseMr.CachedResolve(base.Session); + 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 instructions = new List(); + instructions.Add(processor.Create(OpCodes.Ldarg_0)); //this. + instructions.Add(processor.Create(OpCodes.Call, baseMr)); + processor.InsertFirst(instructions); + } + } + + AddNetworkInitializeExecutedCheck(thisTypeDef, (mdName == NETWORKINITIALIZE_EARLY_INTERNAL_NAME)); + } + + thisTypeDef = TypeDefinitionExtensionsOld.GetNextBaseClassToProcess(thisTypeDef, base.Session); + } while (thisTypeDef != null); + } + + /// + /// Adds returns awake method definitions within awakeDatas. + /// + private void AddReturnsToAwake(List awakeDatas) + { + foreach (AwakeMethodData amd in awakeDatas) + { + ILProcessor processor = amd.AwakeMethodDef.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); + } + } + } + + /// + /// Calls NetworKInitializeLate method on the typeDef. + /// + /// + private void CallNetworkInitializeFromAwake(List awakeDatas, bool callEarly) + { + /* InitializeLate should be called after the user runs + * all their Awake logic. This is so the user can configure + * sync types on Awake and it won't trigger those values + * as needing to be sent over the network, since both + * server and client will be assigning them on Awake. */ + foreach (AwakeMethodData amd in awakeDatas) + { + string methodName = (callEarly) ? NETWORKINITIALIZE_EARLY_INTERNAL_NAME : + NETWORKINITIALIZE_LATE_INTERNAL_NAME; + + TypeDefinition td = amd.AwakeMethodDef.DeclaringType; + MethodReference networkInitMr = td.GetMethodReference(base.Session, methodName); + + ILProcessor processor = amd.AwakeMethodDef.Body.GetILProcessor(); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(networkInitMr.GetCallOpCode(base.Session), networkInitMr); + } + } + + /// + /// Creates an 'NetworkInitialize' method which is called by the childmost class to initialize scripts on Awake. + /// + private void CreateNetworkInitializeMethods(TypeDefinition typeDef, out MethodDefinition networkInitializeIfDisabledMd) + { + CreateMethod(NETWORKINITIALIZE_EARLY_INTERNAL_NAME); + CreateMethod(NETWORKINITIALIZE_LATE_INTERNAL_NAME); + + MethodDefinition baseInitIfDisabled = base.GetClass().NetworkInitializeIfDisabled_MethodRef.CachedResolve(base.Session); + networkInitializeIfDisabledMd = CreateMethod(baseInitIfDisabled.Name, baseInitIfDisabled); + + MethodDefinition CreateMethod(string name, MethodDefinition copied = null) + { + MethodDefinition md; + bool created; + if (copied == null) + md = typeDef.GetOrCreateMethodDefinition(base.Session, name, PUBLIC_VIRTUAL_ATTRIBUTES, typeDef.Module.TypeSystem.Void, out created); + else + md = typeDef.GetOrCreateMethodDefinition(base.Session, name, copied, true, out created); + + if (created) + { + //Emit ret into new method. + ILProcessor processor = md.Body.GetILProcessor(); + //End of method return. + processor.Emit(OpCodes.Ret); + } + + return md; + } + } + + + /// + /// Creates an 'NetworkInitialize' method which is called by the childmost class to initialize scripts on Awake. + /// + private void CallNetworkInitializeMethods(MethodDefinition networkInitializeIfDisabledMd) + { + ILProcessor processor = networkInitializeIfDisabledMd.Body.GetILProcessor(); + + networkInitializeIfDisabledMd.Body.Instructions.Clear(); + CallMethod(NETWORKINITIALIZE_EARLY_INTERNAL_NAME); + CallMethod(NETWORKINITIALIZE_LATE_INTERNAL_NAME); + processor.Emit(OpCodes.Ret); + + void CallMethod(string name) + { + MethodReference initIfDisabledMr = networkInitializeIfDisabledMd.DeclaringType.GetMethodReference(base.Session, name); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(initIfDisabledMr.GetCallOpCode(base.Session), initIfDisabledMr); + } + } + + + /// + /// Creates Awake method for and all parents of typeDef using the parentMostAwakeMethodDef as a template. + /// + /// True if successful. + private bool CreateOrModifyAwakeMethods(TypeDefinition typeDef, ref List datas) + { + //Now update all scopes/create methods. + TypeDefinition copyTypeDef = typeDef; + do + { + bool created; + MethodDefinition awakeMd = copyTypeDef.GetOrCreateMethodDefinition(base.Session, NetworkBehaviourHelper.AWAKE_METHOD_NAME, PUBLIC_VIRTUAL_ATTRIBUTES, copyTypeDef.Module.TypeSystem.Void, out created); + + //Awake is found. Check for invalid return type. + if (!created) + { + if (awakeMd.ReturnType != copyTypeDef.Module.TypeSystem.Void) + { + base.LogError($"IEnumerator Awake methods are not supported within NetworkBehaviours."); + return false; + } + awakeMd.Attributes = PUBLIC_VIRTUAL_ATTRIBUTES; + } + //Aways was made. + else + { + ILProcessor processor = awakeMd.Body.GetILProcessor(); + processor.Emit(OpCodes.Ret); + } + + MethodDefinition logicMd = base.GetClass().CopyIntoNewMethod(awakeMd, $"{NetworkBehaviourHelper.AWAKE_METHOD_NAME}___UserLogic", out _); + //Clear original awake. + awakeMd.Body.Instructions.Clear(); + datas.Add(new AwakeMethodData(awakeMd, logicMd, created)); + + copyTypeDef = TypeDefinitionExtensionsOld.GetNextBaseClassToProcess(copyTypeDef, base.Session); + + } while (copyTypeDef != null); + + + return true; + } + + /// + /// Makes all Awake methods within typeDef and base classes public and virtual. + /// + /// + internal void CreateFirstNetworkInitializeCall(TypeDefinition typeDef, MethodDefinition firstUserAwakeMethodDef, MethodDefinition firstNetworkInitializeMethodDef) + { + ILProcessor processor; + //Get awake for current method. + MethodDefinition thisAwakeMethodDef = typeDef.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME); + bool created = false; + + //If no awake then make one. + if (thisAwakeMethodDef == null) + { + created = true; + + thisAwakeMethodDef = new MethodDefinition(NetworkBehaviourHelper.AWAKE_METHOD_NAME, 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 instructions = new List(); + instructions.Add(processor.Create(OpCodes.Ldarg_0)); //this. + instructions.Add(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); + } + + + } +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourProcessor.cs.meta b/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourProcessor.cs.meta new file mode 100644 index 0000000..1efbb02 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourProcessor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 23866e4d620216745a837fa99e801164 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourSyncProcessor.cs b/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourSyncProcessor.cs new file mode 100644 index 0000000..abb16de --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourSyncProcessor.cs @@ -0,0 +1,1453 @@ +using FishNet.CodeGenerating.Extension; +using FishNet.CodeGenerating.Helping; +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.Configuring; +using FishNet.Object; +using FishNet.Object.Synchronizing; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using FishNet.Transporting; +using MonoFN.Cecil; +using MonoFN.Cecil.Cil; +using MonoFN.Cecil.Rocks; +using MonoFN.Collections.Generic; +using System.Collections.Generic; +using UnityEngine; + +namespace FishNet.CodeGenerating.Processing +{ + internal class NetworkBehaviourSyncProcessor : CodegenBase + { + #region Reflection references. + private TypeDefinition SyncBase_TypeDef; + #endregion + + #region Private. + /// + /// Last instruction to read a sync type. + /// + private Instruction _lastReadInstruction; + /// + /// Sync objects, such as get and set, created during this process. Used to skip modifying created methods. + /// + private List _createdSyncTypeMethodDefinitions = new List(); + /// + /// ReadSyncVar methods which have had their base call already made. + /// + private HashSet _baseCalledReadSyncVars = new HashSet(); + #endregion + + #region Const. + private const string SYNCVAR_PREFIX = "syncVar___"; + private const string ACCESSOR_PREFIX = "sync___"; + private const string SETREGISTERED_METHOD_NAME = "SetRegistered"; + private const string INITIALIZEINSTANCE_METHOD_NAME = "InitializeInstance"; + private const string GETSERIALIZEDTYPE_METHOD_NAME = "GetSerializedType"; + private const string SENDRATE_NAME = "SendRate"; + private const string READPERMISSIONS_NAME = "ReadPermissions"; + #endregion + + public override bool ImportReferences() + { + System.Type syncBaseType = typeof(SyncBase); + SyncBase_TypeDef = base.ImportReference(syncBaseType).Resolve(); + + return true; + } + + /// + /// Processes SyncVars and Objects. + /// + /// + /// + internal bool Process(TypeDefinition typeDef, List<(SyncType, ProcessedSync)> allProcessedSyncs, ref uint syncTypeStartCount) + { + bool modified = false; + _createdSyncTypeMethodDefinitions.Clear(); + _lastReadInstruction = null; + + FieldDefinition[] fieldDefs = typeDef.Fields.ToArray(); + foreach (FieldDefinition fd in fieldDefs) + { + CustomAttribute syncAttribute; + SyncType st = GetSyncType(fd, true, out syncAttribute); + //Not a sync type field. + if (st == SyncType.Unset) + continue; + + if (st == SyncType.Variable) + { + if (TryCreateSyncVar(syncTypeStartCount, allProcessedSyncs, typeDef, fd, syncAttribute)) + syncTypeStartCount++; + } + else if (st == SyncType.List || st == SyncType.HashSet) + { + if (TryCreateSyncList_SyncHashSet(syncTypeStartCount, allProcessedSyncs, typeDef, fd, syncAttribute, st)) + syncTypeStartCount++; + } + else if (st == SyncType.Dictionary) + { + if (TryCreateSyncDictionary(syncTypeStartCount, allProcessedSyncs, typeDef, fd, syncAttribute)) + syncTypeStartCount++; + } + else if (st == SyncType.Custom) + { + if (TryCreateCustom(syncTypeStartCount, allProcessedSyncs, typeDef, fd, syncAttribute)) + syncTypeStartCount++; + } + + modified = true; + } + + return modified; + } + + + /// + /// Gets number of SyncTypes by checking for SyncVar/Object attributes. This does not perform error checking. + /// + /// + /// + internal uint GetSyncTypeCount(TypeDefinition typeDef) + { + uint count = 0; + foreach (FieldDefinition fd in typeDef.Fields) + { + if (HasSyncTypeAttributeUnchecked(fd)) + count++; + } + + return count; + } + + /// + /// Replaces GetSets for methods which may use a SyncType. + /// + internal bool ReplaceGetSets(TypeDefinition typeDef, List<(SyncType, ProcessedSync)> allProcessedSyncs) + { + bool modified = false; + + List modifiableMethods = GetModifiableMethods(typeDef); + modified |= ReplaceGetSetDirties(modifiableMethods, allProcessedSyncs); + + return modified; + } + + /// + /// Gets SyncType fieldDef is. + /// + /// + /// + /// + internal SyncType GetSyncType(FieldDefinition fieldDef, bool validate, out CustomAttribute syncAttribute) + { + syncAttribute = null; + //If the generated field for syncvars ignore it. + if (fieldDef.Name.StartsWith(SYNCVAR_PREFIX)) + return SyncType.Unset; + + bool syncObject; + bool error; + syncAttribute = GetSyncTypeAttribute(fieldDef, out syncObject, out error); + //Do not perform further checks if an error occurred. + if (error) + return SyncType.Unset; + /* If if attribute is null the code must progress + * to throw errors when user creates a sync type + * without using the attribute. */ + if (!validate) + { + return (syncAttribute == null) ? SyncType.Unset : SyncType.Custom; + } + else + { + /* If no attribute make sure the field does not implement + * ISyncType. If it does then a SyncObject or SyncVar attribute + * should exist. */ + if (syncAttribute == null) + { + TypeDefinition foundSyncBaseTd = fieldDef.FieldType.CachedResolve(base.Session).GetClassInInheritance(base.Session, SyncBase_TypeDef); + if (foundSyncBaseTd != null && foundSyncBaseTd.ImplementsInterface()) + base.LogError($"{fieldDef.Name} within {fieldDef.DeclaringType.Name} is a SyncType but is missing the [SyncVar] or [SyncObject] attribute."); + + return SyncType.Unset; + } + + /* If the attribute is not [SyncObject] then the attribute + * is [SyncVar]. Only checks that need to be made is to make sure + * the user is not using a SyncVar attribute when they should be using a SyncObject attribute. */ + if (syncAttribute != null && !syncObject) + { + //Make sure syncvar attribute isnt on a sync object. + if (GetSyncObjectSyncType(syncAttribute) != SyncType.Unset) + { + base.LogError($"{fieldDef.Name} within {fieldDef.DeclaringType.Name} uses a [SyncVar] attribute but should be using [SyncObject]."); + return SyncType.Unset; + } + else + return SyncType.Variable; + } + + /* If here could be syncObject + * or attribute might be null. */ + if (fieldDef.FieldType.CachedResolve(base.Session).ImplementsInterfaceRecursive(base.Session)) + return GetSyncObjectSyncType(syncAttribute); + + SyncType GetSyncObjectSyncType(CustomAttribute sa) + { + //If attribute is null then throw error. + if (sa == null) + { + base.LogError($"{fieldDef.Name} within {fieldDef.DeclaringType.Name} is a SyncType but [SyncObject] attribute was not found."); + return SyncType.Unset; + } + + if (fieldDef.FieldType.Name == base.GetClass().SyncList_Name) + { + return SyncType.List; + } + else if (fieldDef.FieldType.Name == base.GetClass().SyncDictionary_Name) + { + return SyncType.Dictionary; + } + else if (fieldDef.FieldType.Name == base.GetClass().SyncHashSet_Name) + { + return SyncType.HashSet; + } + //Custom types must also implement ICustomSync. + else if (fieldDef.FieldType.CachedResolve(base.Session).ImplementsInterfaceRecursive(base.Session)) + { + return SyncType.Custom; + } + else + { + return SyncType.Unset; + } + } + + //Fall through. + if (syncAttribute != null) + base.LogError($"SyncObject attribute found on {fieldDef.Name} within {fieldDef.DeclaringType.Name} but type {fieldDef.FieldType.Name} does not inherit from SyncBase, or if a custom type does not implement ICustomSync."); + + return SyncType.Unset; + } + + } + + + /// + /// Tries to create a SyncList. + /// + private bool TryCreateCustom(uint syncTypeCount, List<(SyncType, ProcessedSync)> allProcessedSyncs, TypeDefinition typeDef, FieldDefinition originalFieldDef, CustomAttribute syncAttribute) + { + //Get the serialized type. + MethodDefinition getSerialziedTypeMd = originalFieldDef.FieldType.CachedResolve(base.Session).GetMethod(GETSERIALIZEDTYPE_METHOD_NAME); + MethodReference getSerialziedTypeMr = base.ImportReference(getSerialziedTypeMd); + Collection instructions = getSerialziedTypeMr.CachedResolve(base.Session).Body.Instructions; + + bool canSerialize = false; + TypeReference serializedDataTypeRef = null; + /* If the user is returning null then + * they are indicating a custom serializer does not + * have to be implemented. */ + if (instructions.Count == 2 && instructions[0].OpCode == OpCodes.Ldnull && instructions[1].OpCode == OpCodes.Ret) + { + canSerialize = true; + } + //If not returning null then make a serializer for return type. + else + { + foreach (Instruction item in instructions) + { + //This token references the type. + if (item.OpCode == OpCodes.Ldtoken) + { + TypeReference importedTr = null; + if (item.Operand is TypeDefinition td) + importedTr = base.ImportReference(td); + else if (item.Operand is TypeReference tr) + importedTr = base.ImportReference(tr); + + if (importedTr != null) + { + serializedDataTypeRef = importedTr; + canSerialize = base.GetClass().HasSerializerAndDeserializer(serializedDataTypeRef, true); + } + } + } + } + + //Wasn't able to determine serialized type, or create it. + if (!canSerialize) + { + base.LogError($"Custom SyncObject {originalFieldDef.Name} data type {serializedDataTypeRef.FullName} does not support serialization. Use a supported type or create a custom serializer."); + return false; + } + + bool result = InitializeCustom(syncTypeCount, typeDef, originalFieldDef, syncAttribute); + if (result) + allProcessedSyncs.Add((SyncType.Custom, null)); + return result; + } + + + /// + /// Tries to create a SyncList. + /// + private bool TryCreateSyncList_SyncHashSet(uint syncTypeCount, List<(SyncType, ProcessedSync)> allProcessedSyncs, TypeDefinition typeDef, FieldDefinition originalFieldDef, CustomAttribute syncAttribute, SyncType syncType) + { + //Import fieldType to module. + TypeReference fieldTypeTr = base.ImportReference(originalFieldDef.FieldType); + //Make sure type can be serialized. + GenericInstanceType tmpGenerinstanceType = fieldTypeTr as GenericInstanceType; + //this returns the correct data type, eg SyncList would return int. + TypeReference dataTypeRef = base.ImportReference(tmpGenerinstanceType.GenericArguments[0]); + + bool canSerialize = base.GetClass().HasSerializerAndDeserializer(dataTypeRef, true); + if (!canSerialize) + { + base.LogError($"SyncObject {originalFieldDef.Name} data type {dataTypeRef.FullName} does not support serialization. Use a supported type or create a custom serializer."); + return false; + } + + bool result = InitializeSyncList_SyncHashSet(syncTypeCount, typeDef, originalFieldDef, syncAttribute); + if (result) + allProcessedSyncs.Add((syncType, null)); + return result; + } + + /// + /// Tries to create a SyncDictionary. + /// + private bool TryCreateSyncDictionary(uint syncTypeCount, List<(SyncType, ProcessedSync)> allProcessedSyncs, TypeDefinition typeDef, FieldDefinition originalFieldDef, CustomAttribute syncAttribute) + { + //Make sure type can be serialized. + GenericInstanceType tmpGenerinstanceType = originalFieldDef.FieldType as GenericInstanceType; + //this returns the correct data type, eg SyncList would return int. + TypeReference keyTypeRef = tmpGenerinstanceType.GenericArguments[0]; + TypeReference valueTypeRef = tmpGenerinstanceType.GenericArguments[1]; + + bool canSerialize; + //Check key serializer. + canSerialize = base.GetClass().HasSerializerAndDeserializer(keyTypeRef, true); + if (!canSerialize) + { + base.LogError($"SyncObject {originalFieldDef.Name} key type {keyTypeRef.FullName} does not support serialization. Use a supported type or create a custom serializer."); + return false; + } + //Check value serializer. + canSerialize = base.GetClass().HasSerializerAndDeserializer(valueTypeRef, true); + if (!canSerialize) + { + base.LogError($"SyncObject {originalFieldDef.Name} value type {valueTypeRef.FullName} does not support serialization. Use a supported type or create a custom serializer."); + return false; + } + + bool result = InitializeSyncDictionary(syncTypeCount, typeDef, originalFieldDef, syncAttribute); + if (result) + allProcessedSyncs.Add((SyncType.Dictionary, null)); + return result; + } + + + /// + /// Tries to create a SyncVar. + /// + private bool TryCreateSyncVar(uint syncCount, List<(SyncType, ProcessedSync)> allProcessedSyncs, TypeDefinition typeDef, FieldDefinition fieldDef, CustomAttribute syncAttribute) + { + bool canSerialize = base.GetClass().HasSerializerAndDeserializer(fieldDef.FieldType, true); + if (!canSerialize) + { + base.LogError($"SyncVar {fieldDef.FullName} field type {fieldDef.FieldType.FullName} does not support serialization. Use a supported type or create a custom serializer."); + return false; + } + + if (base.Module != typeDef.Module) + { + //Only display warning if field is exposed. + if (!fieldDef.Attributes.HasFlag(FieldAttributes.Private)) + base.Session.DifferentAssemblySyncVars.Add(fieldDef); + return false; + } + + FieldDefinition syncVarFd; + MethodReference accessorSetValueMr; + MethodReference accessorGetValueMr; + + bool created = CreateSyncVar(syncCount, typeDef, fieldDef, syncAttribute, out syncVarFd, out accessorSetValueMr, out accessorGetValueMr); + if (created) + { + FieldReference originalFr = base.ImportReference(fieldDef); + allProcessedSyncs.Add((SyncType.Variable, new ProcessedSync(originalFr, syncVarFd, accessorSetValueMr, accessorGetValueMr))); + } + + return created; + } + + + + /// + /// Returns if fieldDef has a SyncType attribute. No error checking is performed. + /// + /// + /// + private bool HasSyncTypeAttributeUnchecked(FieldDefinition fieldDef) + { + foreach (CustomAttribute customAttribute in fieldDef.CustomAttributes) + { + if (base.GetClass().IsSyncVarAttribute(customAttribute.AttributeType.FullName)) + return true; + else if (base.GetClass().IsSyncObjectAttribute(customAttribute.AttributeType.FullName)) + return true; + } + + return false; + } + + + /// + /// Returns the syncvar attribute on a method, if one exist. Otherwise returns null. + /// + /// + /// + private CustomAttribute GetSyncTypeAttribute(FieldDefinition fieldDef, out bool syncObject, out bool error) + { + CustomAttribute foundAttribute = null; + //Becomes true if an error occurred during this process. + error = false; + syncObject = false; + + foreach (CustomAttribute customAttribute in fieldDef.CustomAttributes) + { + if (base.GetClass().IsSyncVarAttribute(customAttribute.AttributeType.FullName)) + syncObject = false; + else if (base.GetClass().IsSyncObjectAttribute(customAttribute.AttributeType.FullName)) + syncObject = true; + else + continue; + + //A syncvar attribute already exist. + if (foundAttribute != null) + { + base.LogError($"{fieldDef.Name} cannot have multiple SyncType attributes."); + error = true; + } + //Static. + if (fieldDef.IsStatic) + { + base.LogError($"{fieldDef.Name} SyncType cannot be static."); + error = true; + } + //Generic. + if (fieldDef.FieldType.IsGenericParameter) + { + base.LogError($"{fieldDef.Name} SyncType cannot be be generic."); + error = true; + } + //SyncObject readonly check. + if (syncObject && !fieldDef.Attributes.HasFlag(FieldAttributes.InitOnly)) + { + /* If missing readonly see if the user specified + * they want the object to be serialized. */ + bool requireReadOnly = customAttribute.GetField(nameof(SyncObjectAttribute.RequireReadOnly), true); + if (requireReadOnly) + base.LogError($"{fieldDef.Name} SyncObject must be readonly."); + error = true; + } + + + //If all checks passed. + if (!error) + foundAttribute = customAttribute; + } + + //If an error occurred then reset results. + if (error) + foundAttribute = null; + + return foundAttribute; + } + + /// + /// Creates a syncVar class for the user's syncvar. + /// + /// + /// + /// + private bool CreateSyncVar(uint syncCount, TypeDefinition typeDef, FieldDefinition originalFieldDef, CustomAttribute syncTypeAttribute, out FieldDefinition createdSyncVarFd, out MethodReference accessorSetValueMethodRef, out MethodReference accessorGetValueMethodRef) + { + accessorGetValueMethodRef = null; + accessorSetValueMethodRef = null; + CreatedSyncVar createdSyncVar; + createdSyncVarFd = CreateSyncVarFieldDefinition(typeDef, originalFieldDef, out createdSyncVar); + + if (createdSyncVarFd != null) + { + MethodReference hookMr = GetSyncVarHookMethodReference(typeDef, originalFieldDef, syncTypeAttribute); + createdSyncVar.HookMr = hookMr; + + //If accessor was made add it's methods to createdSyncTypeObjects. + if (CreateSyncVarAccessor(originalFieldDef, createdSyncVarFd, createdSyncVar, out accessorGetValueMethodRef, + out accessorSetValueMethodRef, hookMr) != null) + { + _createdSyncTypeMethodDefinitions.Add(accessorGetValueMethodRef.CachedResolve(base.Session)); + _createdSyncTypeMethodDefinitions.Add(accessorSetValueMethodRef.CachedResolve(base.Session)); + } + + InitializeSyncVar(syncCount, createdSyncVarFd, typeDef, originalFieldDef, syncTypeAttribute, createdSyncVar); + + MethodDefinition syncVarReadMd = CreateSyncVarRead(typeDef, syncCount, originalFieldDef, accessorSetValueMethodRef); + if (syncVarReadMd != null) + _createdSyncTypeMethodDefinitions.Add(syncVarReadMd); + + return true; + } + else + { + return false; + } + + } + + /// + /// Creates or gets a SyncType class for originalFieldDef. + /// + /// + private FieldDefinition CreateSyncVarFieldDefinition(TypeDefinition typeDef, FieldDefinition originalFieldDef, out CreatedSyncVar createdSyncVar) + { + createdSyncVar = base.GetClass().GetCreatedSyncVar(originalFieldDef, true); + if (createdSyncVar == null) + return null; + + originalFieldDef.Attributes &= ~FieldAttributes.Private; + originalFieldDef.Attributes |= FieldAttributes.Public; + + FieldDefinition createdFieldDef = new FieldDefinition($"{SYNCVAR_PREFIX}{originalFieldDef.Name}", originalFieldDef.Attributes, createdSyncVar.SyncVarGit); + if (createdFieldDef == null) + { + base.LogError($"Could not create field for Sync type {originalFieldDef.FieldType.FullName}, name of {originalFieldDef.Name}."); + return null; + } + + typeDef.Fields.Add(createdFieldDef); + return createdFieldDef; + } + + /// + /// Validates and gets the hook MethodReference for a SyncVar if available. + /// + /// + /// + /// + /// + private MethodReference GetSyncVarHookMethodReference(TypeDefinition typeDef, FieldDefinition originalFieldDef, CustomAttribute attribute) + { + string hook = attribute.GetField("OnChange", string.Empty); + //No hook is specified. + if (string.IsNullOrEmpty(hook)) + return null; + + MethodDefinition md = typeDef.GetMethod(hook); + + if (md != null) + { + string incorrectParametersMsg = $"OnChange method for {originalFieldDef.FullName} must contain 3 parameters in order of {originalFieldDef.FieldType.Name} oldValue, {originalFieldDef.FieldType.Name} newValue, {base.Module.TypeSystem.Boolean} asServer."; + //Not correct number of parameters. + if (md.Parameters.Count != 3) + { + base.LogError(incorrectParametersMsg); + return null; + } + /* Check if any parameters are not + * the expected type. */ + if (md.Parameters[0].ParameterType.CachedResolve(base.Session) != originalFieldDef.FieldType.CachedResolve(base.Session) || + md.Parameters[1].ParameterType.CachedResolve(base.Session) != originalFieldDef.FieldType.CachedResolve(base.Session) || + md.Parameters[2].ParameterType.CachedResolve(base.Session) != base.Module.TypeSystem.Boolean.CachedResolve(base.Session)) + { + base.LogError(incorrectParametersMsg); + return null; + } + + //If here everything checks out, return a method reference to hook method. + return base.ImportReference(md); + } + //Hook specified but no method found. + else + { + base.LogError($"Could not find method name {hook} for SyncType {originalFieldDef.FullName}."); + return null; + } + } + + /// + /// Creates accessor for a SyncVar. + /// + /// + private FieldDefinition CreateSyncVarAccessor(FieldDefinition originalFd, FieldDefinition createdSyncVarFd, CreatedSyncVar createdSyncVar, out MethodReference accessorGetValueMr, out MethodReference accessorSetValueMr, MethodReference hookMr) + { + /* Create and add property definition. */ + PropertyDefinition createdPropertyDef = new PropertyDefinition($"SyncAccessor_{originalFd.Name}", PropertyAttributes.None, originalFd.FieldType); + createdPropertyDef.DeclaringType = originalFd.DeclaringType; + //add the methods and property to the type. + originalFd.DeclaringType.Properties.Add(createdPropertyDef); + + ILProcessor processor; + + /* Get method for property definition. */ + MethodDefinition createdGetMethodDef = originalFd.DeclaringType.AddMethod($"{ACCESSOR_PREFIX}get_value_{originalFd.Name}", MethodAttributes.Public | + MethodAttributes.SpecialName | MethodAttributes.HideBySig, + originalFd.FieldType); + createdGetMethodDef.SemanticsAttributes = MethodSemanticsAttributes.Getter; + + processor = createdGetMethodDef.Body.GetILProcessor(); + processor.Emit(OpCodes.Ldarg_0); //this. + processor.Emit(OpCodes.Ldfld, originalFd); + processor.Emit(OpCodes.Ret); + accessorGetValueMr = base.ImportReference(createdGetMethodDef); + //Add getter to properties. + createdPropertyDef.GetMethod = createdGetMethodDef; + + /* Set method. */ + //Create the set method + MethodDefinition createdSetMethodDef = originalFd.DeclaringType.AddMethod($"{ACCESSOR_PREFIX}set_value_{originalFd.Name}", MethodAttributes.Public | + MethodAttributes.SpecialName | + MethodAttributes.HideBySig); + createdSetMethodDef.SemanticsAttributes = MethodSemanticsAttributes.Setter; + + ParameterDefinition valueParameterDef = base.GetClass().CreateParameter(createdSetMethodDef, originalFd.FieldType, "value"); + ParameterDefinition calledByUserParameterDef = base.GetClass().CreateParameter(createdSetMethodDef, typeof(bool), "asServer"); + processor = createdSetMethodDef.Body.GetILProcessor(); + + /* Assign to new value. Do this first because SyncVar calls hook + * and value needs to be updated before hook. Only update + * value if calledByUser(asServer) or (!calledByUser && !base.IsServer). + * This ensures clientHost will not overwrite server value. */ + + Instruction afterChangeFieldInst = processor.Create(OpCodes.Nop); + Instruction beforeChangeFieldInst = processor.Create(OpCodes.Nop); + //if (calledByUser || !base.IsServer) + processor.Emit(OpCodes.Ldarg, calledByUserParameterDef); + processor.Emit(OpCodes.Brtrue, beforeChangeFieldInst); + processor.Emit(OpCodes.Ldarg_0); //this. + processor.Emit(OpCodes.Call, base.GetClass().IsServer_MethodRef); + processor.Emit(OpCodes.Brtrue, afterChangeFieldInst); + + // _originalField = value; + processor.Append(beforeChangeFieldInst); + processor.Emit(OpCodes.Ldarg_0); //this. + processor.Emit(OpCodes.Ldarg, valueParameterDef); + processor.Emit(OpCodes.Stfld, originalFd); + processor.Append(afterChangeFieldInst); + + Instruction retInst = processor.Create(OpCodes.Ret); + + if (!Configuration.Configurations.CodeStripping.IsBuilding) + { + processor.Emit(OpCodes.Call, base.GetClass().Application_IsPlaying_MethodRef); + processor.Emit(OpCodes.Brfalse_S, retInst); + } + // SyncVar<>.SetValue(....); + processor.Emit(OpCodes.Ldarg_0); //this. + processor.Emit(OpCodes.Ldfld, createdSyncVarFd); + processor.Emit(OpCodes.Ldarg, valueParameterDef); + processor.Emit(OpCodes.Ldarg, calledByUserParameterDef); + processor.Emit(createdSyncVar.SetValueMr.GetCallOpCode(base.Session), createdSyncVar.SetValueMr); + + processor.Append(retInst); + accessorSetValueMr = base.ImportReference(createdSetMethodDef); + //Add setter to properties. + createdPropertyDef.SetMethod = createdSetMethodDef; + + return originalFd; + } + + /// + /// Sets methods used from SyncBase for typeDef. + /// + /// + internal bool SetSyncBaseMethods(TypeDefinition typeDef, out MethodReference setRegisteredMr, out MethodReference initializeInstanceMr) + { + setRegisteredMr = null; + initializeInstanceMr = null; + //Find the SyncBase class. + TypeDefinition syncBaseTd = null; + TypeDefinition copyTd = typeDef; + do + { + if (copyTd.Name == nameof(SyncBase)) + { + syncBaseTd = copyTd; + break; + } + copyTd = copyTd.GetNextBaseTypeDefinition(base.Session); + } while (copyTd != null); + + //If SyncBase isn't found. + if (syncBaseTd == null) + { + base.LogError($"Could not find SyncBase within type {typeDef.FullName}."); + return false; + } + else + { + //InitializeInstance. + initializeInstanceMr = syncBaseTd.GetMethodReference(base.Session, INITIALIZEINSTANCE_METHOD_NAME); + //SetSyncIndex. + setRegisteredMr = syncBaseTd.GetMethodReference(base.Session, SETREGISTERED_METHOD_NAME); + return true; + } + + } + + /// + /// Initializes a custom SyncObject. + /// + internal bool InitializeCustom(uint syncCount, TypeDefinition typeDef, FieldDefinition originalFieldDef, CustomAttribute attribute) + { + float sendRate = 0.1f; + WritePermission writePermissions = WritePermission.ServerOnly; + ReadPermission readPermissions = ReadPermission.Observers; + Channel channel = Channel.Reliable; + //If attribute isn't null then override values. + if (attribute != null) + { + sendRate = attribute.GetField(SENDRATE_NAME, -1f); + writePermissions = WritePermission.ServerOnly; + readPermissions = attribute.GetField(READPERMISSIONS_NAME, ReadPermission.Observers); + channel = Channel.Reliable; //attribute.GetField("Channel", Channel.Reliable); + } + + //Set needed methods from syncbase. + MethodReference setSyncIndexMr; + MethodReference initializeInstanceMr; + if (!SetSyncBaseMethods(originalFieldDef.FieldType.CachedResolve(base.Session), out setSyncIndexMr, out initializeInstanceMr)) + return false; + + MethodDefinition injectionMethodDef; + ILProcessor processor; + + uint hash = (uint)syncCount; + List insts = new List(); + + /* Initialize with attribute settings. */ + injectionMethodDef = typeDef.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_EARLY_INTERNAL_NAME); + processor = injectionMethodDef.Body.GetILProcessor(); + // + + insts.Add(processor.Create(OpCodes.Ldarg_0)); //this. + insts.Add(processor.Create(OpCodes.Ldfld, originalFieldDef)); + insts.Add(processor.Create(OpCodes.Ldarg_0)); //this again for NetworkBehaviour. + insts.Add(processor.Create(OpCodes.Ldc_I4, (int)hash)); + insts.Add(processor.Create(OpCodes.Ldc_I4, (int)writePermissions)); + insts.Add(processor.Create(OpCodes.Ldc_I4, (int)readPermissions)); + insts.Add(processor.Create(OpCodes.Ldc_R4, sendRate)); + insts.Add(processor.Create(OpCodes.Ldc_I4, (int)channel)); + insts.Add(processor.Create(OpCodes.Ldc_I4_1)); //true for syncObject. + insts.Add(processor.Create(OpCodes.Call, initializeInstanceMr)); + processor.InsertFirst(insts); + + insts.Clear(); + /* Set NetworkBehaviour and SyncIndex to use. */ + injectionMethodDef = typeDef.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_LATE_INTERNAL_NAME); + processor = injectionMethodDef.Body.GetILProcessor(); + // + insts.Add(processor.Create(OpCodes.Ldarg_0)); //this. + insts.Add(processor.Create(OpCodes.Ldfld, originalFieldDef)); + insts.Add(processor.Create(setSyncIndexMr.GetCallOpCode(base.Session), setSyncIndexMr)); + + processor.InsertLast(insts); + + return true; + } + + + + /// + /// Initializes a SyncList. + /// + internal bool InitializeSyncList_SyncHashSet(uint syncCount, TypeDefinition typeDef, FieldDefinition originalFieldDef, CustomAttribute attribute) + { + float sendRate = 0.1f; + WritePermission writePermissions = WritePermission.ServerOnly; + ReadPermission readPermissions = ReadPermission.Observers; + Channel channel = Channel.Reliable; + //If attribute isn't null then override values. + if (attribute != null) + { + sendRate = attribute.GetField(SENDRATE_NAME, -1f); + writePermissions = WritePermission.ServerOnly; + readPermissions = attribute.GetField(READPERMISSIONS_NAME, ReadPermission.Observers); + channel = Channel.Reliable; //attribute.GetField("Channel", Channel.Reliable); + } + + //This import shouldn't be needed but cecil is stingy so rather be safe than sorry. + base.ImportReference(originalFieldDef); + + //Set needed methods from syncbase. + MethodReference setSyncIndexMr; + MethodReference initializeInstanceMr; + if (!SetSyncBaseMethods(originalFieldDef.FieldType.CachedResolve(base.Session), out setSyncIndexMr, out initializeInstanceMr)) + return false; + + MethodDefinition injectionMethodDef; + ILProcessor processor; + + uint hash = (uint)syncCount; + List insts = new List(); + + /* Initialize with attribute settings. */ + injectionMethodDef = typeDef.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_EARLY_INTERNAL_NAME); + processor = injectionMethodDef.Body.GetILProcessor(); + + //InitializeInstance. + insts.Add(processor.Create(OpCodes.Ldarg_0)); //this. + insts.Add(processor.Create(OpCodes.Ldfld, originalFieldDef)); + insts.Add(processor.Create(OpCodes.Ldarg_0)); //this again for NetworkBehaviour. + insts.Add(processor.Create(OpCodes.Ldc_I4, (int)hash)); + insts.Add(processor.Create(OpCodes.Ldc_I4, (int)writePermissions)); + insts.Add(processor.Create(OpCodes.Ldc_I4, (int)readPermissions)); + insts.Add(processor.Create(OpCodes.Ldc_R4, sendRate)); + insts.Add(processor.Create(OpCodes.Ldc_I4, (int)channel)); + insts.Add(processor.Create(OpCodes.Ldc_I4_1)); //true for syncObject. + insts.Add(processor.Create(OpCodes.Call, initializeInstanceMr)); + processor.InsertFirst(insts); + + insts.Clear(); + /* Set NetworkBehaviour and SyncIndex to use. */ + injectionMethodDef = typeDef.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_LATE_INTERNAL_NAME); + processor = injectionMethodDef.Body.GetILProcessor(); + + insts.Add(processor.Create(OpCodes.Ldarg_0)); //this. + insts.Add(processor.Create(OpCodes.Ldfld, originalFieldDef)); + insts.Add(processor.Create(setSyncIndexMr.GetCallOpCode(base.Session), setSyncIndexMr)); + + processor.InsertLast(insts); + + return true; + } + + + + /// + /// Initializes a SyncDictionary. + /// + internal bool InitializeSyncDictionary(uint syncCount, TypeDefinition typeDef, FieldDefinition originalFieldDef, CustomAttribute attribute) + { + float sendRate = 0.1f; + WritePermission writePermissions = WritePermission.ServerOnly; + ReadPermission readPermissions = ReadPermission.Observers; + Channel channel = Channel.Reliable; + //If attribute isn't null then override values. + if (attribute != null) + { + sendRate = attribute.GetField(SENDRATE_NAME, -1f); + writePermissions = WritePermission.ServerOnly; + readPermissions = attribute.GetField(READPERMISSIONS_NAME, ReadPermission.Observers); + channel = Channel.Reliable; //attribute.GetField("Channel", Channel.Reliable); + } + + //This import shouldn't be needed but cecil is stingy so rather be safe than sorry. + base.ImportReference(originalFieldDef); + + //Set needed methods from syncbase. + MethodReference setRegisteredMr; + MethodReference initializeInstanceMr; + if (!SetSyncBaseMethods(originalFieldDef.FieldType.CachedResolve(base.Session), out setRegisteredMr, out initializeInstanceMr)) + return false; + + MethodDefinition injectionMethodDef = typeDef.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_EARLY_INTERNAL_NAME); + ILProcessor processor = injectionMethodDef.Body.GetILProcessor(); + + uint hash = (uint)syncCount; + List insts = new List(); + + /* Initialize with attribute settings. */ + insts.Add(processor.Create(OpCodes.Ldarg_0)); //this. + insts.Add(processor.Create(OpCodes.Ldfld, originalFieldDef)); + insts.Add(processor.Create(OpCodes.Ldarg_0)); //this again for NetworkBehaviour. + insts.Add(processor.Create(OpCodes.Ldc_I4, (int)hash)); + insts.Add(processor.Create(OpCodes.Ldc_I4, (int)writePermissions)); + insts.Add(processor.Create(OpCodes.Ldc_I4, (int)readPermissions)); + insts.Add(processor.Create(OpCodes.Ldc_R4, sendRate)); + insts.Add(processor.Create(OpCodes.Ldc_I4, (int)channel)); + insts.Add(processor.Create(OpCodes.Ldc_I4_1)); //true for syncObject. + insts.Add(processor.Create(OpCodes.Call, initializeInstanceMr)); + processor.InsertFirst(insts); + + insts.Clear(); + /* Set NetworkBehaviour and SyncIndex to use. */ + injectionMethodDef = typeDef.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_LATE_INTERNAL_NAME); + processor = injectionMethodDef.Body.GetILProcessor(); + + insts.Add(processor.Create(OpCodes.Ldarg_0)); //this. + insts.Add(processor.Create(OpCodes.Ldfld, originalFieldDef)); + insts.Add(processor.Create(setRegisteredMr.GetCallOpCode(base.Session), setRegisteredMr)); + + processor.InsertFirst(insts); + + return true; + } + + + /// + /// Initializes a SyncVar<>. + /// + internal void InitializeSyncVar(uint syncCount, FieldDefinition createdFd, TypeDefinition typeDef, FieldDefinition originalFd, CustomAttribute attribute, CreatedSyncVar createdSyncVar) + { + GeneralHelper gh = base.GetClass(); + + //Get all possible attributes. + float sendRate = attribute.GetField(SENDRATE_NAME, -1f); + WritePermission writePermissions = WritePermission.ServerOnly; + ReadPermission readPermissions = attribute.GetField(READPERMISSIONS_NAME, ReadPermission.Observers); + Channel channel = attribute.GetField("Channel", Channel.Reliable); + + MethodDefinition injectionMethodDef = typeDef.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_EARLY_INTERNAL_NAME); + ILProcessor processor = injectionMethodDef.Body.GetILProcessor(); + + uint hash = (uint)syncCount; + List insts = new List(); + //Initialize fieldDef with values from attribute. + insts.Add(processor.Create(OpCodes.Ldarg_0)); //this. + insts.Add(processor.Create(OpCodes.Ldarg_0)); //this again for NetworkBehaviour. + insts.Add(processor.Create(OpCodes.Ldc_I4, (int)hash)); + insts.Add(processor.Create(OpCodes.Ldc_I4, (int)writePermissions)); + insts.Add(processor.Create(OpCodes.Ldc_I4, (int)readPermissions)); + insts.Add(processor.Create(OpCodes.Ldc_R4, sendRate)); + insts.Add(processor.Create(OpCodes.Ldc_I4, (int)channel)); + insts.Add(processor.Create(OpCodes.Ldarg_0)); //this. + + insts.Add(processor.Create(OpCodes.Ldfld, originalFd.MakeHostGenericIfNeeded(base.Session))); //initial value. + insts.Add(processor.Create(OpCodes.Newobj, createdSyncVar.ConstructorMr)); + insts.Add(processor.Create(OpCodes.Stfld, createdFd.MakeHostGenericIfNeeded(base.Session))); + + //If there is a hook method. + if (createdSyncVar.HookMr != null) + { + //SyncVar.add_OnChanged (event). + TypeDefinition svTd = base.GetClass().SyncVar_TypeRef.CachedResolve(base.Session); + GenericInstanceType svGit = svTd.MakeGenericInstanceType(new TypeReference[] { originalFd.FieldType }); + MethodDefinition addMd = svTd.GetMethod("add_OnChange"); + MethodReference syncVarAddMr = addMd.MakeHostInstanceGeneric(base.Session, svGit); + + //Action constructor. + GenericInstanceType actionGit = gh.ActionT3_TypeRef.MakeGenericInstanceType( + originalFd.FieldType, originalFd.FieldType, + base.GetClass().GetTypeReference(typeof(bool))); + MethodReference gitActionCtorMr = gh.ActionT3Constructor_MethodRef.MakeHostInstanceGeneric(base.Session, actionGit); + + // syncVar___field.OnChanged += UserHookMethod; + insts.Add(processor.Create(OpCodes.Ldarg_0)); + insts.Add(processor.Create(OpCodes.Ldfld, createdFd)); + insts.Add(processor.Create(OpCodes.Ldarg_0)); + + //Load the callback function. + MethodDefinition hookMd = createdSyncVar.HookMr.CachedResolve(base.Session); + OpCode ldOpCode; + if (hookMd.IsVirtual) + { + insts.Add(processor.Create(OpCodes.Dup)); + ldOpCode = OpCodes.Ldvirtftn; + } + else + { + ldOpCode = OpCodes.Ldftn; + } + insts.Add(processor.Create(ldOpCode, hookMd)); + + insts.Add(processor.Create(OpCodes.Newobj, gitActionCtorMr)); + insts.Add(processor.Create(syncVarAddMr.GetCallOpCode(base.Session), syncVarAddMr)); + } + processor.InsertFirst(insts); + + insts.Clear(); + /* Set NetworkBehaviour and SyncIndex to use. */ + injectionMethodDef = typeDef.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_LATE_INTERNAL_NAME); + processor = injectionMethodDef.Body.GetILProcessor(); + + //Set NB and SyncIndex to SyncVar<>. + insts.Add(processor.Create(OpCodes.Ldarg_0)); //this. + insts.Add(processor.Create(OpCodes.Ldfld, createdFd)); + insts.Add(processor.Create(createdSyncVar.SetSyncIndexMr.GetCallOpCode(base.Session), createdSyncVar.SetSyncIndexMr)); + + processor.InsertFirst(insts); + } + + /// + /// Replaces GetSets for methods which may use a SyncType. + /// + /// + /// + internal bool ReplaceGetSetDirties(List modifiableMethods, List<(SyncType, ProcessedSync)> processedSyncs) + { + //Build processed syncs into dictionary for quicker loookups. + Dictionary> processedLookup = new Dictionary>(); + foreach ((SyncType st, ProcessedSync ps) in processedSyncs) + { + if (st != SyncType.Variable) + continue; + + List result; + if (!processedLookup.TryGetValue(ps.OriginalFieldRef, out result)) + { + result = new List() { ps }; + processedLookup.Add(ps.OriginalFieldRef, result); + } + + result.Add(ps); + } + + bool modified = false; + foreach (MethodDefinition methodDef in modifiableMethods) + modified |= ReplaceGetSetDirty(methodDef, processedLookup); + + return modified; + } + + /// + /// Replaces GetSets for a method which may use a SyncType. + /// + /// + /// + private bool ReplaceGetSetDirty(MethodDefinition methodDef, Dictionary> processedLookup) + { + if (methodDef == null) + { + base.LogError($"An object expecting value was null. Please try saving your script again."); + return false; + } + if (methodDef.IsAbstract) + return false; + if (_createdSyncTypeMethodDefinitions.Contains(methodDef)) + return false; + if (methodDef.Name == NetworkBehaviourProcessor.NETWORKINITIALIZE_EARLY_INTERNAL_NAME) + return false; + + + bool modified = false; + + for (int i = 0; i < methodDef.Body.Instructions.Count; i++) + { + Instruction inst = methodDef.Body.Instructions[i]; + + /* Loading a field. (Getter) */ + if (inst.OpCode == OpCodes.Ldfld && inst.Operand is FieldReference opFieldld) + { + FieldReference resolvedOpField = opFieldld.CachedResolve(base.Session); + if (resolvedOpField == null) + resolvedOpField = opFieldld.DeclaringType.CachedResolve(base.Session).GetFieldReference(opFieldld.Name, base.Session); + + modified |= ProcessGetField(methodDef, i, resolvedOpField, processedLookup); + } + /* Load address, reference field. */ + else if (inst.OpCode == OpCodes.Ldflda && inst.Operand is FieldReference opFieldlda) + { + FieldReference resolvedOpField = opFieldlda.CachedResolve(base.Session); + if (resolvedOpField == null) + resolvedOpField = opFieldlda.DeclaringType.CachedResolve(base.Session).GetFieldReference(opFieldlda.Name, base.Session); + + modified |= ProcessAddressField(methodDef, i, resolvedOpField, processedLookup); + } + /* Setting a field. (Setter) */ + else if (inst.OpCode == OpCodes.Stfld && inst.Operand is FieldReference opFieldst) + { + FieldReference resolvedOpField = opFieldst.CachedResolve(base.Session); + if (resolvedOpField == null) + resolvedOpField = opFieldst.DeclaringType.CachedResolve(base.Session).GetFieldReference(opFieldst.Name, base.Session); + + modified |= ProcessSetField(methodDef, i, resolvedOpField, processedLookup); + } + + } + + return modified; + } + + /// + /// Replaces Gets for a method which may use a SyncType. + /// + /// + /// + /// + /// + private bool ProcessGetField(MethodDefinition methodDef, int instructionIndex, FieldReference resolvedOpField, Dictionary> processedLookup) + { + Instruction inst = methodDef.Body.Instructions[instructionIndex]; + + //If was a replaced field. + if (processedLookup.TryGetValue(resolvedOpField, out List psLst)) + { + ProcessedSync ps = GetProcessedSync(resolvedOpField, psLst); + if (ps == null) + return false; + //Don't modify the accessor method. + if (ps.GetMethodRef.CachedResolve(base.Session) == methodDef) + return false; + + //Generic type. + if (resolvedOpField.DeclaringType.IsGenericInstance || resolvedOpField.DeclaringType.HasGenericParameters) + { + FieldReference newField = inst.Operand as FieldReference; + GenericInstanceType git = (GenericInstanceType)newField.DeclaringType; + MethodReference syncvarGetMr = ps.GetMethodRef.MakeHostInstanceGeneric(base.Session, git); + inst.OpCode = syncvarGetMr.GetCallOpCode(base.Session); + inst.Operand = syncvarGetMr; + } + //Strong type. + else + { + inst.OpCode = OpCodes.Call; + inst.Operand = ps.GetMethodRef; + } + + return true; + } + else + { + return false; + } + } + + + /// + /// Replaces Sets for a method which may use a SyncType. + /// + /// + /// + /// + /// + private bool ProcessSetField(MethodDefinition methodDef, int instructionIndex, FieldReference resolvedOpField, Dictionary> processedLookup) + { + Instruction inst = methodDef.Body.Instructions[instructionIndex]; + + /* Find any instructions that are jmp/breaking to the one we are modifying. + * These need to be modified to call changed instruction. */ + HashSet brInstructions = new HashSet(); + foreach (Instruction item in methodDef.Body.Instructions) + { + bool canJmp = (item.OpCode == OpCodes.Br || item.OpCode == OpCodes.Brfalse || item.OpCode == OpCodes.Brfalse_S || item.OpCode == OpCodes.Brtrue || item.OpCode == OpCodes.Brtrue_S || item.OpCode == OpCodes.Br_S); + if (!canJmp) + continue; + if (item.Operand == null) + continue; + if (item.Operand is Instruction jmpInst && jmpInst == inst) + brInstructions.Add(item); + } + + //If was a replaced field. + if (processedLookup.TryGetValue(resolvedOpField, out List psLst)) + { + ProcessedSync ps = GetProcessedSync(resolvedOpField, psLst); + if (ps == null) + return false; + //Don't modify the accessor method. + if (ps.SetMethodRef.CachedResolve(base.Session) == methodDef) + return false; + ILProcessor processor = methodDef.Body.GetILProcessor(); + + //Generic type. + if (resolvedOpField.DeclaringType.IsGenericInstance || resolvedOpField.DeclaringType.HasGenericParameters) + { + //Pass in true for as server. + Instruction boolTrueInst = processor.Create(OpCodes.Ldc_I4_1); + methodDef.Body.Instructions.Insert(instructionIndex, boolTrueInst); + + FieldReference newField = inst.Operand as FieldReference; + GenericInstanceType git = (GenericInstanceType)newField.DeclaringType; + inst.OpCode = OpCodes.Call; + inst.Operand = ps.SetMethodRef.MakeHostInstanceGeneric(base.Session, git); + } + //Strong typed. + else + { + + + //Pass in true for as server. + Instruction boolTrueInst = processor.Create(OpCodes.Ldc_I4_1); + methodDef.Body.Instructions.Insert(instructionIndex, boolTrueInst); + inst.OpCode = OpCodes.Call; + inst.Operand = ps.SetMethodRef; + } + + + /* If any instructions are still pointing + * to modified value then they need to be + * redirected to the instruction right above it. + * This is because the boolTrueInst, to indicate + * value is being set as server. */ + foreach (Instruction item in brInstructions) + { + if (item.Operand is Instruction jmpInst && jmpInst == inst) + { + //Use the same index that was passed in, which is now one before modified instruction. + Instruction newInst = methodDef.Body.Instructions[instructionIndex]; + item.Operand = newInst; + } + } + + return true; + } + else + { + return false; + } + } + + /// + /// Replaces address Sets for a method which may use a SyncType. + /// + /// + /// + /// + /// + private bool ProcessAddressField(MethodDefinition methodDef, int instructionIndex, FieldReference resolvedOpField, Dictionary> processedLookup) + { + Instruction inst = methodDef.Body.Instructions[instructionIndex]; + //Check if next instruction is Initobj, which would be setting a new instance. + Instruction nextInstr = inst.Next; + if (nextInstr.OpCode != OpCodes.Initobj) + return false; + + //If was a replaced field. + if (processedLookup.TryGetValue(resolvedOpField, out List psLst)) + { + ProcessedSync ps = GetProcessedSync(resolvedOpField, psLst); + if (ps == null) + return false; + //Don't modify the accessor method. + if (ps.GetMethodRef.CachedResolve(base.Session) == methodDef || ps.SetMethodRef.CachedResolve(base.Session) == methodDef) + return false; + + ILProcessor processor = methodDef.Body.GetILProcessor(); + + VariableDefinition tmpVariableDef = base.GetClass().CreateVariable(methodDef, resolvedOpField.FieldType); + processor.InsertBefore(inst, processor.Create(OpCodes.Ldloca, tmpVariableDef)); + processor.InsertBefore(inst, processor.Create(OpCodes.Initobj, resolvedOpField.FieldType)); + processor.InsertBefore(inst, processor.Create(OpCodes.Ldloc, tmpVariableDef)); + Instruction newInstr = processor.Create(OpCodes.Call, ps.SetMethodRef); + processor.InsertBefore(inst, newInstr); + + /* Pass in true for as server. + * The instruction index is 3 past ld. */ + Instruction boolTrueInst = processor.Create(OpCodes.Ldc_I4_1); + methodDef.Body.Instructions.Insert(instructionIndex + 3, boolTrueInst); + + processor.Remove(inst); + processor.Remove(nextInstr); + + return true; + } + else + { + return false; + } + } + + /// + /// Calls ReadSyncVar going up the hierarchy. + /// + /// + internal void CallBaseReadSyncVar(TypeDefinition firstTypeDef) + { + string readSyncVarName = base.GetClass().ReadSyncVar_MethodRef.Name; + //TypeDef which needs to make the base call. + MethodDefinition callerMd = null; + TypeDefinition copyTd = firstTypeDef; + do + { + MethodDefinition readMd; + + readMd = copyTd.GetMethod(readSyncVarName); + if (readMd != null) + callerMd = readMd; + + /* If baseType exist and it's not networkbehaviour + * look into calling the ReadSyncVar method. */ + if (copyTd.BaseType != null && copyTd.BaseType.FullName != base.GetClass().FullName) + { + readMd = copyTd.BaseType.CachedResolve(base.Session).GetMethod(readSyncVarName); + //Not all classes will have syncvars to read. + if (!_baseCalledReadSyncVars.Contains(callerMd) && readMd != null && callerMd != null) + { + MethodReference baseReadMr = copyTd.GetMethodReferenceInBase(base.Session, readSyncVarName);// readMd.GetMethodReferenceInBase (base.Session, base.ImportReference(readMd); + ILProcessor processor = callerMd.Body.GetILProcessor(); + ParameterDefinition asServerPd = callerMd.Parameters[2]; + /* Calls base.ReadSyncVar and if result is true + * then exit methods. This is because a true return means the base + * was able to process the syncvar. */ + List baseCallInsts = new List(); + Instruction skipBaseReturn = processor.Create(OpCodes.Nop); + baseCallInsts.Add(processor.Create(OpCodes.Ldarg_0)); //This. + baseCallInsts.Add(processor.Create(OpCodes.Ldarg_1)); //PooledReader. + baseCallInsts.Add(processor.Create(OpCodes.Ldarg_2)); //Index. + baseCallInsts.Add(processor.Create(OpCodes.Ldarg, asServerPd)); //AsServer. + baseCallInsts.Add(processor.Create(OpCodes.Call, baseReadMr)); + baseCallInsts.Add(processor.Create(OpCodes.Brfalse_S, skipBaseReturn)); + baseCallInsts.Add(processor.Create(OpCodes.Ldc_I4_1)); + baseCallInsts.Add(processor.Create(OpCodes.Ret)); + baseCallInsts.Add(skipBaseReturn); + processor.InsertFirst(baseCallInsts); + + _baseCalledReadSyncVars.Add(callerMd); + } + } + + copyTd = TypeDefinitionExtensionsOld.GetNextBaseClassToProcess(copyTd, base.Session); + + } while (copyTd != null); + + } + + /// + /// Reads a PooledReader locally then sets value to the SyncVars accessor. + /// + /// + /// + /// + private MethodDefinition CreateSyncVarRead(TypeDefinition typeDef, uint syncIndex, FieldDefinition originalFieldDef, MethodReference accessorSetMethodRef) + { + Instruction jmpGoalInst; + ILProcessor processor; + + //Get the read sync method, or create it if not present. + MethodDefinition readSyncMethodDef = typeDef.GetMethod(base.GetClass().ReadSyncVar_MethodRef.Name); + if (readSyncMethodDef == null) + { + readSyncMethodDef = new MethodDefinition(base.GetClass().ReadSyncVar_MethodRef.Name, + (MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual), + typeDef.Module.TypeSystem.Void); + readSyncMethodDef.ReturnType = base.GetClass().GetTypeReference(typeof(bool)); + + base.GetClass().CreateParameter(readSyncMethodDef, typeof(PooledReader)); + base.GetClass().CreateParameter(readSyncMethodDef, typeof(uint)); + base.GetClass().CreateParameter(readSyncMethodDef, typeof(bool)); + readSyncMethodDef.Body.InitLocals = true; + + processor = readSyncMethodDef.Body.GetILProcessor(); + //Return false as fall through. + processor.Emit(OpCodes.Ldc_I4_0); + processor.Emit(OpCodes.Ret); + + typeDef.Methods.Add(readSyncMethodDef); + } + //Already created. + else + { + processor = readSyncMethodDef.Body.GetILProcessor(); + } + + ParameterDefinition pooledReaderPd = readSyncMethodDef.Parameters[0]; + ParameterDefinition indexPd = readSyncMethodDef.Parameters[1]; + ParameterDefinition asServerPd = readSyncMethodDef.Parameters[2]; + VariableDefinition nextValueVariableDef; + List readInsts; + + /* Create a nop instruction placed at the first index of the method. + * All instructions will be added before this, then the nop will be + * removed afterwards. This ensures the newer instructions will + * be above the previous. This let's the IL jump to a previously + * created read instruction when the latest one fails conditions. */ + Instruction nopPlaceHolderInst = processor.Create(OpCodes.Nop); + + readSyncMethodDef.Body.Instructions.Insert(0, nopPlaceHolderInst); + + /* If there was a previously made read then set jmp goal to the first + * condition for it. Otherwise set it to the last instruction, which would + * be a ret. Keep in mind if ret has a value we must go back 2 index + * rather than one. */ + jmpGoalInst = (_lastReadInstruction != null) ? _lastReadInstruction : + readSyncMethodDef.Body.Instructions[readSyncMethodDef.Body.Instructions.Count - 2]; + + //Check index first. if (index != syncIndex) return + Instruction nextLastReadInstruction = processor.Create(OpCodes.Ldarg, indexPd); + processor.InsertBefore(jmpGoalInst, nextLastReadInstruction); + + uint hash = (uint)syncIndex; + processor.InsertBefore(jmpGoalInst, processor.Create(OpCodes.Ldc_I4, (int)hash)); + //processor.InsertBefore(jmpGoalInst, processor.Create(OpCodes.Ldc_I4, syncIndex)); + processor.InsertBefore(jmpGoalInst, processor.Create(OpCodes.Bne_Un, jmpGoalInst)); + //PooledReader.ReadXXXX() + readInsts = base.GetClass().CreateRead(readSyncMethodDef, pooledReaderPd, + originalFieldDef.FieldType, out nextValueVariableDef); + if (readInsts == null) + return null; + //Add each instruction from CreateRead. + foreach (Instruction i in readInsts) + processor.InsertBefore(jmpGoalInst, i); + + //Call accessor with new value and passing in asServer. + processor.InsertBefore(jmpGoalInst, processor.Create(OpCodes.Ldarg_0)); //this. + processor.InsertBefore(jmpGoalInst, processor.Create(OpCodes.Ldloc, nextValueVariableDef)); + //processor.InsertBefore(jmpGoalInst, processor.Create(OpCodes.Ldc_I4_0)); + processor.InsertBefore(jmpGoalInst, processor.Create(OpCodes.Ldarg, asServerPd)); + processor.InsertBefore(jmpGoalInst, processor.Create(OpCodes.Call, accessorSetMethodRef)); + //Return true when able to process. + processor.InsertBefore(jmpGoalInst, processor.Create(OpCodes.Ldc_I4_1)); + processor.InsertBefore(jmpGoalInst, processor.Create(OpCodes.Ret)); + + _lastReadInstruction = nextLastReadInstruction; + processor.Remove(nopPlaceHolderInst); + + return readSyncMethodDef; + } + + /// + /// Returns methods which may be modified by code generation. + /// + /// + /// + private List GetModifiableMethods(TypeDefinition typeDef) + { + List results = new List(); + + CheckTypeDefinition(typeDef); + //Have to add nested types because this are where courotines are stored. + foreach (TypeDefinition nestedTd in typeDef.NestedTypes) + CheckTypeDefinition(nestedTd); + + void CheckTypeDefinition(TypeDefinition td) + { + foreach (MethodDefinition methodDef in td.Methods) + { + if (methodDef.Name == ".cctor") + continue; + if (methodDef.IsConstructor) + continue; + if (methodDef.Body == null) + continue; + + results.Add(methodDef); + } + + foreach (PropertyDefinition propertyDef in td.Properties) + { + if (propertyDef.GetMethod != null) + results.Add(propertyDef.GetMethod); + if (propertyDef.SetMethod != null) + results.Add(propertyDef.SetMethod); + } + } + + return results; + } + + /// + /// Returns the ProcessedSync entry for resolvedOpField. + /// + /// + /// + /// + private ProcessedSync GetProcessedSync(FieldReference resolvedOpField, List psLst) + { + for (int i = 0; i < psLst.Count; i++) + { + if (psLst[i].OriginalFieldRef == resolvedOpField) + return psLst[i]; + } + + /* Fall through, not found. */ + base.LogError($"Unable to find user referenced field for {resolvedOpField.Name}."); + return null; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourSyncProcessor.cs.meta b/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourSyncProcessor.cs.meta new file mode 100644 index 0000000..1ee148f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourSyncProcessor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ec95af37f78b9e340b5eaa199c1af94a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Processing/Prediction.meta b/Assets/FishNet/CodeGenerating/Processing/Prediction.meta new file mode 100644 index 0000000..00224f1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Prediction.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +<<<<<<<< HEAD:Assets/FishNet/CodeGenerating/Processing/Prediction.meta +guid: 07f594347080e6547b13253aa03402e3 +folderAsset: yes +======== +guid: 89b00a3741f72ac4e861a08cf1202228 +>>>>>>>> origin/3-pre-3.1:Assets/Test/InitializeOrder/InitializeOrder_Test.unity.meta +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Processing/Prediction/PredictionProcessor.cs b/Assets/FishNet/CodeGenerating/Processing/Prediction/PredictionProcessor.cs new file mode 100644 index 0000000..a31463b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Prediction/PredictionProcessor.cs @@ -0,0 +1,960 @@ +using FishNet.CodeGenerating.Extension; +using FishNet.CodeGenerating.Helping; +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.Connection; +using FishNet.Object; +using FishNet.Object.Prediction; +using FishNet.Object.Prediction.Delegating; +using FishNet.Serializing; +using FishNet.Serializing.Helping; +using FishNet.Transporting; +using MonoFN.Cecil; +using MonoFN.Cecil.Cil; +using MonoFN.Cecil.Rocks; +using System.Collections.Generic; +using UnityEngine; +using SR = System.Reflection; + +namespace FishNet.CodeGenerating.Processing +{ + internal class PredictionProcessor : CodegenBase + { + #region Types. + private enum InsertType + { + First, + Last, + Current + } + + private class CreatedPredictionFields + { + /// + /// Delegate for calling replicate user logic. + /// + public readonly FieldReference ReplicateULDelegate; + /// + /// Delegate for calling replicate user logic. + /// + public readonly FieldReference ReconcileULDelegate; + /// + /// Replicate data buffered on the server. + /// + public readonly FieldReference ServerReplicateDatas; + /// + /// Replicate data buffered on the client. + /// + public readonly FieldReference ClientReplicateDatas; + /// + /// Last reconcile data received from the server. + /// + public readonly FieldReference ReconcileData; + /// + /// A buffer to read replicates into. + /// + public readonly FieldReference ServerReplicateReaderBuffer; + + public CreatedPredictionFields(FieldReference replicateULDelegate, FieldReference reconcileULDelegate, FieldReference serverReplicateDatas, FieldReference clientReplicateDatas, FieldReference reconcileData, + FieldReference serverReplicateReaderBuffer) + { + ReplicateULDelegate = replicateULDelegate; + ReconcileULDelegate = reconcileULDelegate; + ServerReplicateDatas = serverReplicateDatas; + ClientReplicateDatas = clientReplicateDatas; + ReconcileData = reconcileData; + ServerReplicateReaderBuffer = serverReplicateReaderBuffer; + } + } + + private class PredictionReaders + { + public MethodReference ReplicateReader; + public MethodReference ReconcileReader; + + public PredictionReaders(MethodReference replicateReader, MethodReference reconcileReader) + { + ReplicateReader = replicateReader; + ReconcileReader = reconcileReader; + } + } + + #endregion + + #region Public. + public string IReplicateData_FullName = typeof(IReplicateData).FullName; + public string IReconcileData_FullName = typeof(IReconcileData).FullName; + public TypeReference ReplicateULDelegate_TypeRef; + public TypeReference ReconcileULDelegate_TypeRef; + public MethodReference IReplicateData_GetTick_MethodRef; + public MethodReference IReplicateData_SetTick_MethodRef; + public MethodReference IReconcileData_GetTick_MethodRef; + public MethodReference IReconcileData_SetTick_MethodRef; + public MethodReference Unity_GetGameObject_MethodRef; + #endregion + + #region Const. + public const string REPLICATE_LOGIC_PREFIX = "Logic_Replicate___"; + public const string REPLICATE_READER_PREFIX = "Reader_Replicate___"; + public const string RECONCILE_LOGIC_PREFIX = "Logic_Reconcile___"; + public const string RECONCILE_READER_PREFIX = "Reader_Reconcile___"; + #endregion + + public override bool ImportReferences() + { + System.Type locType; + SR.MethodInfo locMi; + + ReplicateULDelegate_TypeRef = base.ImportReference(typeof(ReplicateUserLogicDelegate<>)); + ReconcileULDelegate_TypeRef = base.ImportReference(typeof(ReconcileUserLogicDelegate<>)); + + //GetGameObject. + locMi = typeof(UnityEngine.Component).GetMethod("get_gameObject"); + Unity_GetGameObject_MethodRef = base.ImportReference(locMi); + + //Get/Set tick. + locType = typeof(IReplicateData); + foreach (SR.MethodInfo mi in locType.GetMethods()) + { + if (mi.Name == nameof(IReplicateData.GetTick)) + IReplicateData_GetTick_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(IReplicateData.SetTick)) + IReplicateData_SetTick_MethodRef = base.ImportReference(mi); + } + + locType = typeof(IReconcileData); + foreach (SR.MethodInfo mi in locType.GetMethods()) + { + if (mi.Name == nameof(IReconcileData.GetTick)) + IReconcileData_GetTick_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(IReconcileData.SetTick)) + IReconcileData_SetTick_MethodRef = base.ImportReference(mi); + } + + return true; + } + + internal bool Process(TypeDefinition typeDef, ref uint rpcCount) + { + bool modified = false; + modified |= ProcessLocal(typeDef, ref rpcCount); + + return modified; + } + + #region Setup and checks. + /// + /// Gets number of predictions by checking for prediction attributes. This does not perform error checking. + /// + /// + /// + internal uint GetPredictionCount(TypeDefinition typeDef) + { + /* Currently only one prediction method is allowed per typeDef. + * Return 1 soon as a method is found. */ + foreach (MethodDefinition methodDef in typeDef.Methods) + { + foreach (CustomAttribute customAttribute in methodDef.CustomAttributes) + { + if (customAttribute.Is(base.GetClass().ReplicateAttribute_FullName)) + return 1; + } + } + + return 0; + } + + + /// + /// Ensures only one prediction and reconile method exist per typeDef, and outputs finding. + /// + /// True if there is only one set of prediction methods. False if none, or more than one set. + internal bool GetPredictionMethods(TypeDefinition typeDef, out MethodDefinition replicateMd, out MethodDefinition reconcileMd) + { + replicateMd = null; + reconcileMd = null; + + bool error = false; + foreach (MethodDefinition methodDef in typeDef.Methods) + { + foreach (CustomAttribute customAttribute in methodDef.CustomAttributes) + { + if (customAttribute.Is(base.GetClass().ReplicateAttribute_FullName)) + { + if (!MethodIsPrivate(methodDef) || AlreadyFound(replicateMd)) + error = true; + else + replicateMd = methodDef; + } + else if (customAttribute.Is(base.GetClass().ReconcileAttribute_FullName)) + { + if (!MethodIsPrivate(methodDef) || AlreadyFound(reconcileMd)) + error = true; + else + reconcileMd = methodDef; + } + if (error) + break; + } + if (error) + break; + } + + bool MethodIsPrivate(MethodDefinition md) + { + bool isPrivate = md.Attributes.HasFlag(MethodAttributes.Private); + if (!isPrivate) + base.LogError($"Method {md.Name} within {typeDef.Name} is a prediction method and must be private."); + return isPrivate; + } + + bool AlreadyFound(MethodDefinition md) + { + bool alreadyFound = (md != null); + if (alreadyFound) + base.LogError($"{typeDef.Name} contains multiple prediction sets; currently only one set is allowed."); + + return alreadyFound; + } + + if (!error && ((replicateMd == null) != (reconcileMd == null))) + { + base.LogError($"{typeDef.Name} must contain both a [Replicate] and [Reconcile] method when using prediction."); + error = true; + } + + if (error || (replicateMd == null) || (reconcileMd == null)) + return false; + else + return true; + } + #endregion + + private bool ProcessLocal(TypeDefinition typeDef, ref uint rpcCount) + { + MethodDefinition replicateMd; + MethodDefinition reconcileMd; + + //Not using prediction methods. + if (!GetPredictionMethods(typeDef, out replicateMd, out reconcileMd)) + return false; + + //If replication methods found but this hierarchy already has max. + if (rpcCount >= NetworkBehaviourHelper.MAX_RPC_ALLOWANCE) + { + base.LogError($"{typeDef.FullName} and inherited types exceed {NetworkBehaviourHelper.MAX_RPC_ALLOWANCE} replicated methods. Only {NetworkBehaviourHelper.MAX_RPC_ALLOWANCE} replicated methods are supported per inheritance hierarchy."); + return false; + } + + bool parameterError = false; + parameterError |= HasParameterError(replicateMd, typeDef, true); + parameterError |= HasParameterError(reconcileMd, typeDef, false); + if (parameterError) + return false; + + TypeDefinition replicateDataTd = replicateMd.Parameters[0].ParameterType.CachedResolve(base.Session); + TypeDefinition reconcileDataTd = reconcileMd.Parameters[0].ParameterType.CachedResolve(base.Session); + //Ensure datas implement interfaces. + bool interfacesImplemented = true; + DataImplementInterfaces(replicateMd, true, ref interfacesImplemented); + DataImplementInterfaces(reconcileMd, false, ref interfacesImplemented); + if (!interfacesImplemented) + return false; + if (!TickFieldIsNonSerializable(replicateDataTd, true)) + return false; + if (!TickFieldIsNonSerializable(reconcileDataTd, false)) + return false; + + /* Make sure data can serialize. Use array type, this will + * generate a serializer for element type as well. */ + bool canSerialize; + //Make sure replicate data can serialize. + canSerialize = base.GetClass().HasSerializerAndDeserializer(replicateDataTd.MakeArrayType(), true); + if (!canSerialize) + { + base.LogError($"Replicate data type {replicateDataTd.Name} does not support serialization. Use a supported type or create a custom serializer."); + return false; + } + //Make sure reconcile data can serialize. + canSerialize = base.GetClass().HasSerializerAndDeserializer(reconcileDataTd, true); + if (!canSerialize) + { + base.LogError($"Reconcile data type {reconcileDataTd.Name} does not support serialization. Use a supported type or create a custom serializer."); + return false; + } + //Creates fields for buffers. + CreatedPredictionFields predictionFields; + CreateFields(typeDef, replicateMd, reconcileMd, out predictionFields); + + PredictionReaders predictionReaders; + MethodDefinition replicateULMd; + MethodDefinition reconcileULMd; + CreatePredictionMethods(typeDef, replicateMd, reconcileMd, predictionFields, rpcCount, out predictionReaders, out replicateULMd, out reconcileULMd); + InitializeCollections(typeDef, replicateMd, predictionFields); + InitializeULDelegates(typeDef, predictionFields, replicateMd, reconcileMd, replicateULMd, reconcileULMd); + RegisterRpcs(typeDef, rpcCount, predictionReaders); + + rpcCount++; + return true; + } + + /// + /// Ensures the tick field for GetTick is non-serializable. + /// + /// + /// + private bool TickFieldIsNonSerializable(TypeDefinition dataTd, bool replicate) + { + string methodName = (replicate) ? IReplicateData_GetTick_MethodRef.Name : IReconcileData_GetTick_MethodRef.Name; + MethodDefinition getMd = dataTd.GetMethod(methodName); + + //Try to find ldFld. + Instruction ldFldInst = null; + foreach (Instruction item in getMd.Body.Instructions) + { + if (item.OpCode == OpCodes.Ldfld) + { + ldFldInst = item; + break; + } + } + + //If ldFld not found. + if (ldFldInst == null) + { + base.LogError($"{dataTd.FullName} method {getMd.Name} does not return a field type for the Tick. Make a new private field of uint type and return it's value within {getMd.Name}."); + return false; + } + //Make sure the field is private. + else + { + FieldDefinition fd = (FieldDefinition)ldFldInst.Operand; + if (!fd.Attributes.HasFlag(FieldAttributes.Private)) + { + base.LogError($"{dataTd.FullName} method {getMd.Name} returns a tick field but it's not marked as private. Make the field {fd.Name} private."); + return false; + } + } + + //All checks pass. + return true; + } + + private void DataImplementInterfaces(MethodDefinition methodDef, bool isReplicate, ref bool interfacesImplemented) + { + TypeReference dataTr = methodDef.Parameters[0].ParameterType; + string interfaceName = (isReplicate) ? IReplicateData_FullName : IReconcileData_FullName; + //If does not implement. + if (!dataTr.CachedResolve(base.Session).ImplementsInterfaceRecursive(base.Session, interfaceName)) + { + string name = (isReplicate) ? typeof(IReplicateData).Name : typeof(IReconcileData).Name; + base.LogError($"Prediction data type {dataTr.Name} for method {methodDef.Name} in class {methodDef.DeclaringType.Name} must implement the {name} interface."); + interfacesImplemented = false; + } + } + + /// + /// Registers RPCs that prediction uses. + /// + private void RegisterRpcs(TypeDefinition typeDef, uint hash, PredictionReaders readers) + { + MethodDefinition injectionMethodDef = typeDef.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_EARLY_INTERNAL_NAME); + ILProcessor processor = injectionMethodDef.Body.GetILProcessor(); + List insts = new List(); + + Register(readers.ReplicateReader.CachedResolve(base.Session), true); + Register(readers.ReconcileReader.CachedResolve(base.Session), false); + + void Register(MethodDefinition readerMd, bool replicate) + { + insts.Add(processor.Create(OpCodes.Ldarg_0)); + insts.Add(processor.Create(OpCodes.Ldc_I4, (int)hash)); + /* Create delegate and call NetworkBehaviour method. */ + insts.Add(processor.Create(OpCodes.Ldarg_0)); + insts.Add(processor.Create(OpCodes.Ldftn, readerMd)); + + MethodReference ctorMr; + MethodReference callMr; + if (replicate) + { + ctorMr = base.GetClass().ReplicateRpcDelegateConstructor_MethodRef; + callMr = base.GetClass().RegisterReplicateRpc_MethodRef; + } + else + { + ctorMr = base.GetClass().ReconcileRpcDelegateConstructor_MethodRef; + callMr = base.GetClass().RegisterReconcileRpc_MethodRef; + } + + insts.Add(processor.Create(OpCodes.Newobj, ctorMr)); + insts.Add(processor.Create(OpCodes.Call, callMr)); + } + + processor.InsertLast(insts); + } + + /// + /// Initializes collection fields made during this process. + /// + /// + private void InitializeCollections(TypeDefinition typeDef, MethodDefinition replicateMd, CreatedPredictionFields predictionFields) + { + GeneralHelper gh = base.GetClass(); + TypeReference replicateDataTr = replicateMd.Parameters[0].ParameterType; + MethodDefinition injectionMethodDef = typeDef.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_EARLY_INTERNAL_NAME); + ILProcessor processor = injectionMethodDef.Body.GetILProcessor(); + + Generate(predictionFields.ClientReplicateDatas, true); + Generate(predictionFields.ServerReplicateDatas, false); + + void Generate(FieldReference fr, bool isList) + { + MethodDefinition ctorMd = base.GetClass().List_TypeRef.CachedResolve(base.Session).GetConstructor(); + GenericInstanceType collectionGit; + if (isList) + gh.GetGenericLists(replicateDataTr, out collectionGit); + else + gh.GetGenericQueues(replicateDataTr, out collectionGit); + MethodReference ctorMr = ctorMd.MakeHostInstanceGeneric(base.Session, collectionGit); + + List insts = new List(); + + insts.Add(processor.Create(OpCodes.Ldarg_0)); + insts.Add(processor.Create(OpCodes.Newobj, ctorMr)); + insts.Add(processor.Create(OpCodes.Stfld, fr)); + processor.InsertFirst(insts); + } + } + + /// + /// Initializes collection fields made during this process. + /// + /// + private void InitializeULDelegates(TypeDefinition typeDef, CreatedPredictionFields predictionFields, MethodDefinition replicateMd, MethodDefinition reconcileMd, MethodDefinition replicateULMd, MethodDefinition reconcileULMd) + { + TypeReference replicateDataTr = replicateMd.Parameters[0].ParameterType; + TypeReference reconcileDataTr = reconcileMd.Parameters[0].ParameterType; + MethodDefinition injectionMethodDef = typeDef.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_EARLY_INTERNAL_NAME); + ILProcessor processor = injectionMethodDef.Body.GetILProcessor(); + List insts = new List(); + + Generate(replicateULMd, replicateDataTr, predictionFields.ReplicateULDelegate, typeof(ReplicateUserLogicDelegate<>), ReplicateULDelegate_TypeRef); + Generate(reconcileULMd, reconcileDataTr, predictionFields.ReconcileULDelegate, typeof(ReconcileUserLogicDelegate<>), ReconcileULDelegate_TypeRef); + + void Generate(MethodDefinition ulMd, TypeReference dataTr, FieldReference fr, System.Type delegateType, TypeReference delegateTr) + { + insts.Clear(); + + MethodDefinition ctorMd = delegateTr.CachedResolve(base.Session).GetFirstConstructor(base.Session, true); + GenericInstanceType collectionGit; + GetGenericULDelegate(dataTr, delegateType, out collectionGit); + MethodReference ctorMr = ctorMd.MakeHostInstanceGeneric(base.Session, collectionGit); + + insts.Add(processor.Create(OpCodes.Ldarg_0)); + insts.Add(processor.Create(OpCodes.Ldarg_0)); + insts.Add(processor.Create(OpCodes.Ldftn, ulMd)); + insts.Add(processor.Create(OpCodes.Newobj, ctorMr)); + insts.Add(processor.Create(OpCodes.Stfld, fr)); + processor.InsertFirst(insts); + } + } + + + + /// + /// Creates field buffers for replicate datas. + /// + /// + /// + /// + /// + private void CreateFields(TypeDefinition typeDef, MethodDefinition replicateMd, MethodDefinition reconcileMd, out CreatedPredictionFields predictionFields) + { + GeneralHelper gh = base.GetClass(); + TypeReference replicateDataTr = replicateMd.Parameters[0].ParameterType; + TypeReference replicateDataArrTr = replicateDataTr.MakeArrayType(); + TypeReference reconcileDataTr = reconcileMd.Parameters[0].ParameterType; + + GenericInstanceType replicateULDelegateGit; + GenericInstanceType reconcileULDelegateGit; + GenericInstanceType lstDataGit; + GenericInstanceType queueDataGit; + GetGenericULDelegate(replicateDataTr, typeof(ReplicateUserLogicDelegate<>), out replicateULDelegateGit); + GetGenericULDelegate(reconcileDataTr, typeof(ReconcileUserLogicDelegate<>), out reconcileULDelegateGit); + gh.GetGenericLists(replicateDataTr, out lstDataGit); + gh.GetGenericQueues(replicateDataTr, out queueDataGit); + + /* Data buffer. */ + FieldDefinition replicateULDelegateFd = new FieldDefinition($"_replicateULDelegate___{replicateMd.Name}", FieldAttributes.Private, replicateULDelegateGit); + FieldDefinition reconcileULDelegateFd = new FieldDefinition($"_reconcileULDelegate___{reconcileMd.Name}", FieldAttributes.Private, reconcileULDelegateGit); + FieldDefinition serverReplicatesFd = new FieldDefinition($"_serverReplicates___{replicateMd.Name}", FieldAttributes.Private, queueDataGit); + FieldDefinition clientReplicatesFd = new FieldDefinition($"_clientReplicates___{replicateMd.Name}", FieldAttributes.Private, lstDataGit); + FieldDefinition reconcileDataFd = new FieldDefinition($"_reconcileData___{replicateMd.Name}", FieldAttributes.Private, reconcileDataTr); + FieldDefinition serverReplicatesReadBufferFd = new FieldDefinition($"{replicateMd.Name}___serverReplicateReadBuffer", FieldAttributes.Private, replicateDataArrTr); + + typeDef.Fields.Add(replicateULDelegateFd); + typeDef.Fields.Add(reconcileULDelegateFd); + typeDef.Fields.Add(serverReplicatesFd); + typeDef.Fields.Add(clientReplicatesFd); + typeDef.Fields.Add(reconcileDataFd); + typeDef.Fields.Add(serverReplicatesReadBufferFd); + + predictionFields = new CreatedPredictionFields(replicateULDelegateFd, reconcileULDelegateFd, serverReplicatesFd, clientReplicatesFd, reconcileDataFd, + serverReplicatesReadBufferFd); + } + + /// + /// Returns if there are any errors with the prediction methods parameters and will print if so. + /// + private bool HasParameterError(MethodDefinition methodDef, TypeDefinition typeDef, bool replicateMethod) + { + //Replicate: data, asServer, channel, replaying. + //Reconcile: data, asServer, channel. + int count = (replicateMethod) ? 4 : 3; + + //Check parameter count. + if (methodDef.Parameters.Count != count) + { + PrintParameterExpectations(); + return true; + } + + //Data check. + if (!methodDef.Parameters[0].ParameterType.IsClassOrStruct(base.Session)) + { + base.LogError($"Prediction methods must use a class or structure as the first parameter type. Structures are recommended to avoid allocations."); + return true; + } + //asServer + if (methodDef.Parameters[1].ParameterType.Name != typeof(bool).Name) + { + PrintParameterExpectations(); + return true; + } + //Channel. + if (methodDef.Parameters[2].ParameterType.Name != typeof(Channel).Name) + { + PrintParameterExpectations(); + return true; + } + if (replicateMethod) + { + //replaying + if (methodDef.Parameters[3].ParameterType.Name != typeof(bool).Name) + { + PrintParameterExpectations(); + return true; + } + + } + + void PrintParameterExpectations() + { + if (replicateMethod) + base.LogError($"Replicate method {methodDef.Name} within {typeDef.Name} requires exactly {count} parameters. In order: replicate data, asServer boolean, channel = Channel.Unreliable, replaying boolean."); + else + base.LogError($"Reconcile method {methodDef.Name} within {typeDef.Name} requires exactly {count} parameters. In order: replicate data, asServer boolean, channel = Channel.Unreliable."); + } + + //No errors with parameters. + return false; + } + + /// + /// Creates all methods needed for a RPC. + /// + /// + /// + /// + private bool CreatePredictionMethods(TypeDefinition typeDef, MethodDefinition replicateMd, MethodDefinition reconcileMd, CreatedPredictionFields predictionFields, uint rpcCount, out PredictionReaders predictionReaders, out MethodDefinition replicateULMd, out MethodDefinition reconcileULMd) + { + GeneralHelper gh = base.GetClass(); + NetworkBehaviourHelper nbh = base.GetClass(); + predictionReaders = null; + + string copySuffix = "___UL"; + replicateULMd = base.GetClass().CopyIntoNewMethod(replicateMd, $"{replicateMd.Name}{copySuffix}", out _); + reconcileULMd = base.GetClass().CopyIntoNewMethod(reconcileMd, $"{reconcileMd.Name}{copySuffix}", out _); + replicateMd.Body.Instructions.Clear(); + reconcileMd.Body.Instructions.Clear(); + + MethodDefinition replicateReader; + MethodDefinition reconcileReader; + + if (!CreateReplicate()) + return false; + if (!CreateReconcile()) + return false; + + CreateClearReplicateCacheMethod(typeDef, replicateMd.Parameters[0].ParameterType, predictionFields); + CreateReplicateReader(typeDef, replicateMd, predictionFields, out replicateReader); + CreateReconcileReader(typeDef, reconcileMd, predictionFields, out reconcileReader); + predictionReaders = new PredictionReaders(replicateReader, reconcileReader); + + bool CreateReplicate() + { + ILProcessor processor = replicateMd.Body.GetILProcessor(); + ParameterDefinition replicateDataPd = replicateMd.Parameters[0]; + MethodDefinition comparerMd = gh.CreateEqualityComparer(replicateDataPd.ParameterType); + gh.CreateIsDefaultComparer(replicateDataPd.ParameterType, comparerMd); + ParameterDefinition asServerPd = replicateMd.Parameters[1]; + ParameterDefinition replayingPd = replicateMd.Parameters[3]; + + Instruction exitMethodInst = processor.Create(OpCodes.Nop); + //Exit early conditions. + processor.Emit(OpCodes.Ldarg_0); //base. + processor.Emit(OpCodes.Ldarg, asServerPd); + processor.Emit(OpCodes.Ldarg, replayingPd); + processor.Emit(OpCodes.Call, base.GetClass().Replicate_ExitEarly_A_MethodRef); + processor.Emit(OpCodes.Brtrue, exitMethodInst); + + //Wrap server content in an asServer if statement. + Instruction notAsServerInst = processor.Create(OpCodes.Nop); + processor.Emit(OpCodes.Ldarg, asServerPd); + processor.Emit(OpCodes.Brfalse, notAsServerInst); + /***************************/ + ServerCreateReplicate(replicateMd, predictionFields); + processor.Emit(OpCodes.Br, exitMethodInst); + /***************************/ + + //Wrap client content in an !asServer if statement. + processor.Append(notAsServerInst); + /***************************/ + ClientCreateReplicate(replicateMd, predictionFields, rpcCount); + /***************************/ + + processor.Append(exitMethodInst); + processor.Emit(OpCodes.Ret); + + return true; + } + + + bool CreateReconcile() + { + ILProcessor processor = reconcileMd.Body.GetILProcessor(); + ParameterDefinition reconcileDataPd = reconcileMd.Parameters[0]; + ParameterDefinition asServerPd = reconcileMd.Parameters[1]; + ParameterDefinition channelPd = reconcileMd.Parameters[2]; + TypeReference replicateDataTr = replicateMd.Parameters[0].ParameterType; + + //ExitEarly A. + Instruction exitMethodInst = processor.Create(OpCodes.Nop); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldarg, asServerPd); + processor.Emit(OpCodes.Ldarga, channelPd); + processor.Emit(OpCodes.Call, base.GetClass().Reconcile_ExitEarly_A_MethodRef); + processor.Emit(OpCodes.Brtrue, exitMethodInst); + + //Wrap server content in an asServer if statement. + Instruction notAsServerInst = processor.Create(OpCodes.Nop); + processor.Emit(OpCodes.Ldarg, asServerPd); + processor.Emit(OpCodes.Brfalse, notAsServerInst); + /***************************/ + ServerCreateReconcile(reconcileMd, predictionFields, ref rpcCount); + /***************************/ + processor.Emit(OpCodes.Br, exitMethodInst); + + processor.Append(notAsServerInst); + + MethodReference reconcileClientGim = nbh.Reconcile_Client_MethodRef.GetMethodReference( + base.Session, new TypeReference[] { reconcileDataPd.ParameterType, replicateDataTr }); + //(ReplicateULDelegate replicateDel, ReconcileULDelegate reconcileDel, List collection, + //T data, Channel channel) where T : IReconcileData + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ReconcileULDelegate); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ReplicateULDelegate); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ClientReplicateDatas); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ReconcileData); + processor.Emit(OpCodes.Ldarg, channelPd); + processor.Emit(OpCodes.Call, reconcileClientGim); + + processor.Append(exitMethodInst); + processor.Emit(OpCodes.Ret); + return true; + } + + return true; + } + + #region Universal prediction. + /// + /// Creates an override for the method responsible for resetting replicates. + /// + /// + /// + private void CreateClearReplicateCacheMethod(TypeDefinition typeDef, TypeReference dataTr, CreatedPredictionFields predictionFields) + { + GeneralHelper gh = base.GetClass(); + string clearDatasName = base.GetClass().ClearReplicateCache_Method_Name; + MethodDefinition md = typeDef.GetMethod(clearDatasName); + + //Already exist when it shouldn't. + if (md != null) + { + base.LogWarning($"{typeDef.Name} overrides method {md.Name} when it should not. Logic within {md.Name} will be replaced by code generation."); + md.Body.Instructions.Clear(); + } + else + { + md = new MethodDefinition(clearDatasName, (MethodAttributes.Public | MethodAttributes.Virtual), base.Module.TypeSystem.Void); + gh.CreateParameter(md, typeof(bool), "asServer"); + typeDef.Methods.Add(md); + base.ImportReference(md); + } + + ILProcessor processor = md.Body.GetILProcessor(); + + GenericInstanceType dataListGit; + gh.GetGenericLists(dataTr, out dataListGit); + //Get clear method. + MethodReference lstClearMr = gh.List_Clear_MethodRef.MakeHostInstanceGeneric(base.Session, dataListGit); + ParameterDefinition asServerPd = md.Parameters[0]; + + Instruction afterAsServerInst = processor.Create(OpCodes.Nop); + Instruction resetTicksInst = processor.Create(OpCodes.Nop); + + processor.Emit(OpCodes.Ldarg, asServerPd); + processor.Emit(OpCodes.Brfalse_S, afterAsServerInst); + + //Clear on server replicates. + MethodReference clrQueueMr = base.ImportReference(typeof(NetworkBehaviour).GetMethod(nameof(NetworkBehaviour.ClearQueue_Server_Internal))); + GenericInstanceMethod clrQueueGim = clrQueueMr.MakeGenericMethod(new TypeReference[] { dataTr }); + + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ServerReplicateDatas); + processor.Emit(clrQueueMr.GetCallOpCode(base.Session), clrQueueGim); + processor.Emit(OpCodes.Br_S, resetTicksInst); + processor.Append(afterAsServerInst); + //Clear on client replicates. + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ClientReplicateDatas); + processor.Emit(lstClearMr.GetCallOpCode(base.Session), lstClearMr); + + processor.Append(resetTicksInst); + processor.Emit(OpCodes.Ret); + } + + /// + /// Outputs generic ReplicateULDelegate for dataTr. + /// + private void GetGenericULDelegate(TypeReference dataTr, System.Type delegateType, out GenericInstanceType git) + { + TypeReference delDataTr = base.ImportReference(delegateType); + git = delDataTr.MakeGenericInstanceType(new TypeReference[] { dataTr }); + } + + /// + /// Subtracts 1 from a field. + /// + private List SubtractFromField(MethodDefinition methodDef, FieldDefinition fieldDef) + { + List insts = new List(); + ILProcessor processor = methodDef.Body.GetILProcessor(); + + // _field--; + insts.Add(processor.Create(OpCodes.Ldarg_0)); + insts.Add(processor.Create(OpCodes.Ldarg_0)); + insts.Add(processor.Create(OpCodes.Ldfld, fieldDef)); + insts.Add(processor.Create(OpCodes.Ldc_I4_1)); + insts.Add(processor.Create(OpCodes.Sub)); + insts.Add(processor.Create(OpCodes.Stfld, fieldDef)); + + return insts; + } + /// + /// Subtracts 1 from a variable. + /// + private List SubtractFromVariable(MethodDefinition methodDef, VariableDefinition variableDef) + { + List insts = new List(); + ILProcessor processor = methodDef.Body.GetILProcessor(); + + // variable--; + insts.Add(processor.Create(OpCodes.Ldloc, variableDef)); + insts.Add(processor.Create(OpCodes.Ldc_I4_1)); + insts.Add(processor.Create(OpCodes.Sub)); + insts.Add(processor.Create(OpCodes.Stloc, variableDef)); + + return insts; + } + + /// + /// Subtracts 1 from a variable. + /// + private List SubtractOneVariableFromAnother(MethodDefinition methodDef, VariableDefinition srcVd, VariableDefinition modifierVd) + { + List insts = new List(); + ILProcessor processor = methodDef.Body.GetILProcessor(); + + // variable -= v2; + insts.Add(processor.Create(OpCodes.Ldloc, srcVd)); + insts.Add(processor.Create(OpCodes.Ldloc, modifierVd)); + insts.Add(processor.Create(OpCodes.Sub)); + insts.Add(processor.Create(OpCodes.Stloc, srcVd)); + + return insts; + } + #endregion + + #region Server side. + /// + /// Creates replicate code for client. + /// + private void ServerCreateReplicate(MethodDefinition replicateMd, CreatedPredictionFields predictionFields) + { + ILProcessor processor = replicateMd.Body.GetILProcessor(); + + ParameterDefinition replicateDataPd = replicateMd.Parameters[0]; + ParameterDefinition channelPd = replicateMd.Parameters[2]; + TypeReference replicateDataTr = replicateDataPd.ParameterType; + + GenericInstanceMethod replicateGim = base.GetClass().Replicate_Server_MethodRef.MakeGenericMethod(new TypeReference[] { replicateDataTr }); + + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ReplicateULDelegate); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ServerReplicateDatas); + processor.Emit(OpCodes.Ldarg, channelPd); + processor.Emit(OpCodes.Call, replicateGim); + } + + /// + /// Creates a reader for replicate data received from clients. + /// + private bool CreateReplicateReader(TypeDefinition typeDef, MethodDefinition replicateMd, CreatedPredictionFields predictionFields, out MethodDefinition result) + { + string methodName = $"{REPLICATE_READER_PREFIX}{replicateMd.Name}"; + MethodDefinition createdMd = new MethodDefinition(methodName, + MethodAttributes.Private, + replicateMd.Module.TypeSystem.Void); + typeDef.Methods.Add(createdMd); + createdMd.Body.InitLocals = true; + + ILProcessor processor = createdMd.Body.GetILProcessor(); + + GeneralHelper gh = base.GetClass(); + NetworkBehaviourHelper nbh = base.GetClass(); + + TypeReference dataTr = replicateMd.Parameters[0].ParameterType; + //Create parameters. + ParameterDefinition readerPd = gh.CreateParameter(createdMd, typeof(PooledReader)); + ParameterDefinition networkConnectionPd = gh.CreateParameter(createdMd, typeof(NetworkConnection)); + ParameterDefinition channelPd = gh.CreateParameter(createdMd, typeof(Channel)); + + MethodReference replicateReaderGim = nbh.Replicate_Reader_MethodRef.GetMethodReference(base.Session, dataTr); + + processor.Emit(OpCodes.Ldarg_0); + //Reader, NetworkConnection. + processor.Emit(OpCodes.Ldarg, readerPd); + processor.Emit(OpCodes.Ldarg, networkConnectionPd); + //arrBuffer. + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ServerReplicateReaderBuffer); + //replicates. + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ServerReplicateDatas); + //Channel. + processor.Emit(OpCodes.Ldarg, channelPd); + processor.Emit(OpCodes.Call, replicateReaderGim); + //Add end of method. + processor.Emit(OpCodes.Ret); + result = createdMd; + return true; + } + + /// + /// Creates server side code for reconcileMd. + /// + /// + /// + private void ServerCreateReconcile(MethodDefinition reconcileMd, CreatedPredictionFields predictionFields, ref uint rpcCount) + { + ParameterDefinition reconcileDataPd = reconcileMd.Parameters[0]; + ParameterDefinition channelPd = reconcileMd.Parameters[2]; + ILProcessor processor = reconcileMd.Body.GetILProcessor(); + + GenericInstanceMethod methodGim = base.GetClass().Reconcile_Server_MethodRef.MakeGenericMethod(new TypeReference[] { reconcileDataPd.ParameterType }); + + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldc_I4, (int)rpcCount); + processor.Emit(OpCodes.Ldarg, reconcileDataPd); + processor.Emit(OpCodes.Ldarg, channelPd); + processor.Emit(OpCodes.Call, methodGim); + + rpcCount++; + } + #endregion + + #region Client side. + /// + /// Creates replicate code for client. + /// + private void ClientCreateReplicate(MethodDefinition replicateMd, CreatedPredictionFields predictionFields, uint rpcCount) + { + ParameterDefinition dataPd = replicateMd.Parameters[0]; + ParameterDefinition channelPd = replicateMd.Parameters[2]; + TypeReference dataTr = dataPd.ParameterType; + + ILProcessor processor = replicateMd.Body.GetILProcessor(); + + //Make method reference NB.SendReplicateRpc + GenericInstanceMethod replicateClientGim = base.GetClass().Replicate_Client_MethodRef.MakeGenericMethod(new TypeReference[] { dataTr }); + processor.Emit(OpCodes.Ldarg_0);//base. + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ReplicateULDelegate); + processor.Emit(OpCodes.Ldc_I4, (int)rpcCount); + processor.Emit(OpCodes.Ldarg_0);//this. + processor.Emit(OpCodes.Ldfld, predictionFields.ClientReplicateDatas.CachedResolve(base.Session)); + processor.Emit(OpCodes.Ldarg, dataPd); + processor.Emit(OpCodes.Ldarg, channelPd); + processor.Emit(OpCodes.Call, replicateClientGim); + } + + /// + /// Creates a reader for replicate data received from clients. + /// + private bool CreateReconcileReader(TypeDefinition typeDef, MethodDefinition reconcileMd, CreatedPredictionFields predictionFields, out MethodDefinition result) + { + string methodName = $"{RECONCILE_READER_PREFIX}{reconcileMd.Name}"; + MethodDefinition createdMd = new MethodDefinition(methodName, + MethodAttributes.Private, + reconcileMd.Module.TypeSystem.Void); + typeDef.Methods.Add(createdMd); + createdMd.Body.InitLocals = true; + + ILProcessor processor = createdMd.Body.GetILProcessor(); + + GeneralHelper gh = base.GetClass(); + NetworkBehaviourHelper nbh = base.GetClass(); + + TypeReference dataTr = reconcileMd.Parameters[0].ParameterType; + //Create parameters. + ParameterDefinition readerPd = gh.CreateParameter(createdMd, typeof(PooledReader)); + ParameterDefinition channelPd = gh.CreateParameter(createdMd, typeof(Channel)); + + MethodReference methodGim = nbh.Reconcile_Reader_MethodRef.GetMethodReference(base.Session, dataTr); + + processor.Emit(OpCodes.Ldarg_0); + //Reader, data, channel. + processor.Emit(OpCodes.Ldarg, readerPd); + //Data to assign read value to. + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldflda, predictionFields.ReconcileData); + //Channel. + processor.Emit(OpCodes.Ldarg, channelPd); + processor.Emit(OpCodes.Call, methodGim); + //Add end of method. + processor.Emit(OpCodes.Ret); + + result = createdMd; + return true; + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Processing/Prediction/PredictionProcessor.cs.meta b/Assets/FishNet/CodeGenerating/Processing/Prediction/PredictionProcessor.cs.meta new file mode 100644 index 0000000..c8266bc --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Prediction/PredictionProcessor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d0663606e86b0b34bae85df164747e72 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Processing/QOLAttributeProcessor.cs b/Assets/FishNet/CodeGenerating/Processing/QOLAttributeProcessor.cs new file mode 100644 index 0000000..dd5f02d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/QOLAttributeProcessor.cs @@ -0,0 +1,164 @@ +using FishNet.CodeGenerating.Helping; +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.CodeGenerating.Processing.Rpc; +using FishNet.Configuring; +using FishNet.Managing.Logging; +using MonoFN.Cecil; +using MonoFN.Cecil.Cil; +using System.Collections.Generic; +using System.Linq; + +namespace FishNet.CodeGenerating.Processing +{ + internal class QolAttributeProcessor : CodegenBase + { + + internal bool Process(TypeDefinition typeDef, bool moveStrippedCalls) + { + bool modified = false; + List methods = typeDef.Methods.ToList(); + + + + foreach (MethodDefinition md in methods) + { + //Has RPC attribute, doesn't quality for a quality of life attribute. + if (base.GetClass().Attributes.HasRpcAttributes(md)) + continue; + + QolAttributeType qolType; + CustomAttribute qolAttribute = GetQOLAttribute(md, out qolType); + if (qolAttribute == null) + continue; + + /* This is a one time check to make sure the qolType is + * a supported value. Multiple methods beyond this rely on the + * value being supported. Rather than check in each method a + * single check is performed here. */ + if (qolType != QolAttributeType.Server && qolType != QolAttributeType.Client) + { + base.LogError($"QolAttributeType of {qolType.ToString()} is unhandled."); + continue; + } + + CreateAttributeMethod(md, qolAttribute, qolType); + modified = true; + } + + return modified; + } + + /// + /// Returns the RPC attribute on a method, if one exist. Otherwise returns null. + /// + /// + /// + /// + private CustomAttribute GetQOLAttribute(MethodDefinition methodDef, out QolAttributeType qolType) + { + CustomAttribute foundAttribute = null; + qolType = QolAttributeType.None; + //Becomes true if an error occurred during this process. + bool error = false; + //Nothing to check. + if (methodDef == null || methodDef.CustomAttributes == null) + return null; + + foreach (CustomAttribute customAttribute in methodDef.CustomAttributes) + { + QolAttributeType thisQolType = base.GetClass().GetQolAttributeType(customAttribute.AttributeType.FullName); + if (thisQolType != QolAttributeType.None) + { + //A qol attribute already exist. + if (foundAttribute != null) + { + base.LogError($"{methodDef.Name} {thisQolType.ToString()} method cannot have multiple quality of life attributes."); + error = true; + } + ////Static method. + //if (methodDef.IsStatic) + //{ + // CodegenSession.AddError($"{methodDef.Name} {thisQolType.ToString()} method cannot be static."); + // error = true; + //} + //Abstract method. + if (methodDef.IsAbstract) + { + base.LogError($"{methodDef.Name} {thisQolType.ToString()} method cannot be abstract."); + error = true; + } + + //If all checks passed. + if (!error) + { + foundAttribute = customAttribute; + qolType = thisQolType; + } + } + } + + //If an error occurred then reset results. + if (error) + { + foundAttribute = null; + qolType = QolAttributeType.None; + } + + return foundAttribute; + } + + /// + /// Modifies the specified method to use QolType. + /// + private void CreateAttributeMethod(MethodDefinition methodDef, CustomAttribute qolAttribute, QolAttributeType qolType) + { + bool inheritsNetworkBehaviour = methodDef.DeclaringType.InheritsNetworkBehaviour(base.Session); + + //True to use InstanceFInder. + bool useStatic = (methodDef.IsStatic || !inheritsNetworkBehaviour); + + if (qolType == QolAttributeType.Client) + { + if (!StripMethod(methodDef)) + { + LoggingType logging = qolAttribute.GetField("Logging", LoggingType.Warning); + /* Since isClient also uses insert first + * it will be put ahead of the IsOwner check, since the + * codegen processes it after IsOwner. EG... + * IsOwner will be added first, then IsClient will be added first over IsOwner. */ + bool requireOwnership = qolAttribute.GetField("RequireOwnership", false); + if (requireOwnership && useStatic) + { + base.LogError($"Method {methodDef.Name} has a [Client] attribute which requires ownership but the method may not use this attribute. Either the method is static, or the script does not inherit from NetworkBehaviour."); + return; + } + //If (!base.IsOwner); + if (requireOwnership) + base.GetClass().CreateLocalClientIsOwnerCheck(methodDef, logging, true, false, true); + //Otherwise normal IsClient check. + else + base.GetClass().CreateIsClientCheck(methodDef, logging, useStatic, true); + } + } + else if (qolType == QolAttributeType.Server) + { + if (!StripMethod(methodDef)) + { + LoggingType logging = qolAttribute.GetField("Logging", LoggingType.Warning); + base.GetClass().CreateIsServerCheck(methodDef, logging, useStatic, true); + } + } + + bool StripMethod(MethodDefinition md) + { + + + //Fall through. + return false; + } + } + + + } + +} diff --git a/Assets/FishNet/CodeGenerating/Processing/QOLAttributeProcessor.cs.meta b/Assets/FishNet/CodeGenerating/Processing/QOLAttributeProcessor.cs.meta new file mode 100644 index 0000000..ff1a0e7 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/QOLAttributeProcessor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5080d7597ffca904b9a9fd5926e4e5a6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Processing/ReaderProcessor.cs b/Assets/FishNet/CodeGenerating/Processing/ReaderProcessor.cs new file mode 100644 index 0000000..c6abcb6 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/ReaderProcessor.cs @@ -0,0 +1,1114 @@ +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.Serializing; +using MonoFN.Cecil; +using MonoFN.Cecil.Cil; +using MonoFN.Cecil.Rocks; +using SR = System.Reflection; +using System.Collections.Generic; +using System; +using FishNet.CodeGenerating.ILCore; +using FishNet.CodeGenerating.Extension; +using FishNet.Utility.Performance; +using FishNet.Object; + +namespace FishNet.CodeGenerating.Helping +{ + + internal class ReaderProcessor : CodegenBase + { + + #region Reflection references. + public TypeDefinition GeneratedReaderClassTypeDef; + public MethodDefinition GeneratedReaderOnLoadMethodDef; + public readonly Dictionary InstancedReaderMethods = new Dictionary(); + public readonly Dictionary StaticReaderMethods = new Dictionary(); + public HashSet AutoPackedMethods = new HashSet(new TypeReferenceComparer()); + #endregion + + #region Misc. + /// + /// TypeReferences which have already had delegates made for. + /// + private HashSet _delegatedTypes = new HashSet(); + #endregion + + #region Const. + /// + /// Namespace to use for generated serializers and delegates. + /// + public const string GENERATED_READER_NAMESPACE = WriterProcessor.GENERATED_WRITER_NAMESPACE; + /// + /// Name to use for generated serializers class. + /// + public const string GENERATED_WRITERS_CLASS_NAME = "GeneratedReaders___Internal"; + /// + /// Attributes to use for generated serializers class. + /// + public const TypeAttributes GENERATED_TYPE_ATTRIBUTES = (TypeAttributes.BeforeFieldInit | TypeAttributes.Class | TypeAttributes.AnsiClass | + TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.Abstract | TypeAttributes.Sealed); + /// + /// Name to use for InitializeOnce method. + /// + public const string INITIALIZEONCE_METHOD_NAME = WriterProcessor.INITIALIZEONCE_METHOD_NAME; + /// + /// Attributes to use for InitializeOnce method within generated serializer classes. + /// + public const MethodAttributes INITIALIZEONCE_METHOD_ATTRIBUTES = WriterProcessor.INITIALIZEONCE_METHOD_ATTRIBUTES; + /// + /// Attritbutes to use for generated serializers. + /// + public const MethodAttributes GENERATED_METHOD_ATTRIBUTES = WriterProcessor.GENERATED_METHOD_ATTRIBUTES; + /// + /// Prefix used which all instanced and user created serializers should start with. + /// + internal const string READ_PREFIX = "Read"; + /// + /// Class name to use for generated readers. + /// + internal const string GENERATED_READERS_CLASS_NAME = "GeneratedReaders___Internal"; + /// + /// Prefix to use for generated readers. + /// + private const string GENERATED_READ_PREFIX = "Read___"; + /// + /// Types to exclude from being scanned for auto serialization. + /// + public static System.Type[] EXCLUDED_AUTO_SERIALIZER_TYPES => WriterProcessor.EXCLUDED_AUTO_SERIALIZER_TYPES; + /// + /// Types to exclude from being scanned for auto serialization. + /// + public static string[] EXCLUDED_ASSEMBLY_PREFIXES => WriterProcessor.EXCLUDED_ASSEMBLY_PREFIXES; + #endregion + + public override bool ImportReferences() => true; + + public bool Process() + { + GeneralHelper gh = base.GetClass(); + + CreateGeneratedClassData(); + FindInstancedReaders(); + CreateInstancedReaderExtensions(); + + void CreateGeneratedClassData() + { + GeneratedReaderClassTypeDef = gh.GetOrCreateClass(out _, ReaderProcessor.GENERATED_TYPE_ATTRIBUTES, ReaderProcessor.GENERATED_READERS_CLASS_NAME, null, WriterProcessor.GENERATED_WRITER_NAMESPACE); + /* If constructor isn't set then try to get or create it + * and also add it to methods if were created. */ + GeneratedReaderOnLoadMethodDef = gh.GetOrCreateMethod(GeneratedReaderClassTypeDef, out _, INITIALIZEONCE_METHOD_ATTRIBUTES, INITIALIZEONCE_METHOD_NAME, base.Module.TypeSystem.Void); + gh.CreateRuntimeInitializeOnLoadMethodAttribute(GeneratedReaderOnLoadMethodDef); + + ILProcessor ppp = GeneratedReaderOnLoadMethodDef.Body.GetILProcessor(); + ppp.Emit(OpCodes.Ret); + //GeneratedReaderOnLoadMethodDef.DeclaringType.Methods.Remove(GeneratedReaderOnLoadMethodDef); + } + + void FindInstancedReaders() + { + Type pooledWriterType = typeof(PooledReader); + foreach (SR.MethodInfo methodInfo in pooledWriterType.GetMethods()) + { + if (IsSpecialReadMethod(methodInfo)) + continue; + bool autoPackMethod; + if (IsIgnoredReadMethod(methodInfo, out autoPackMethod)) + continue; + + MethodReference methodRef = base.ImportReference(methodInfo); + /* TypeReference for the return type + * of the read method. */ + TypeReference typeRef = base.ImportReference(methodRef.ReturnType); + + /* If here all checks pass. */ + AddReaderMethod(typeRef, methodRef, true, true); + if (autoPackMethod) + AutoPackedMethods.Add(typeRef); + } + } + + return true; + } + + + /// + /// Returns if a MethodInfo is considered a special write method. + /// Special read methods have declared references within this class, and will not have extensions made for them. + /// + public bool IsSpecialReadMethod(SR.MethodInfo methodInfo) + { + /* Special methods. */ + if (methodInfo.Name == nameof(PooledReader.ReadPackedWhole)) + return true; + else if (methodInfo.Name == nameof(PooledReader.ReadArray)) + return true; + else if (methodInfo.Name == nameof(PooledReader.ReadDictionary)) + return true; + + return false; + } + + /// + /// Returns if a read method should be ignored. + /// + public bool IsIgnoredReadMethod(SR.MethodInfo methodInfo, out bool autoPackMethod) + { + autoPackMethod = false; + + if (base.GetClass().CodegenExclude(methodInfo)) + return true; + //Not long enough to be a write method. + else if (methodInfo.Name.Length < READ_PREFIX.Length) + return true; + //Method name doesn't start with writePrefix. + else if (methodInfo.Name.Substring(0, READ_PREFIX.Length) != READ_PREFIX) + return true; + SR.ParameterInfo[] parameterInfos = methodInfo.GetParameters(); + //Can have at most one parameter for packing. + if (parameterInfos.Length > 1) + return true; + //If has one parameter make sure it's a packing type. + if (parameterInfos.Length == 1) + { + autoPackMethod = (parameterInfos[0].ParameterType == typeof(AutoPackType)); + if (!autoPackMethod) + return true; + } + + return false; + } + + + + /// + /// Adds typeRef, methodDef to instanced or readerMethods. + /// + /// + /// + /// + internal void AddReaderMethod(TypeReference typeRef, MethodReference methodRef, bool instanced, bool useAdd) + { + string fullName = typeRef.GetFullnameWithoutBrackets(); + Dictionary dict = (instanced) ? + InstancedReaderMethods : StaticReaderMethods; + + if (useAdd) + dict.Add(fullName, methodRef); + else + dict[fullName] = methodRef; + } + + + /// + /// Creates a Read delegate for readMethodRef and places it within the generated reader/writer constructor. + /// + /// + /// + internal void CreateReadDelegate(MethodReference readMr, bool isStatic) + { + GeneralHelper gh = base.GetClass(); + ReaderImports ri = base.GetClass(); + + if (!isStatic) + { + //Supporting Write with types containing generics is more trouble than it's worth. + if (readMr.IsGenericInstance || readMr.HasGenericParameters) + return; + } + + //Check if ret already exist, if so remove it; ret will be added on again in this method. + if (GeneratedReaderOnLoadMethodDef.Body.Instructions.Count != 0) + { + int lastIndex = (GeneratedReaderOnLoadMethodDef.Body.Instructions.Count - 1); + if (GeneratedReaderOnLoadMethodDef.Body.Instructions[lastIndex].OpCode == OpCodes.Ret) + GeneratedReaderOnLoadMethodDef.Body.Instructions.RemoveAt(lastIndex); + } + //Check if already exist. + ILProcessor processor = GeneratedReaderOnLoadMethodDef.Body.GetILProcessor(); + TypeReference dataTypeRef = readMr.ReturnType; + if (_delegatedTypes.Contains(dataTypeRef)) + { + base.LogError($"Generic read already created for {dataTypeRef.FullName}."); + return; + } + else + { + _delegatedTypes.Add(dataTypeRef); + } + + //Create a Func delegate + processor.Emit(OpCodes.Ldnull); + processor.Emit(OpCodes.Ldftn, readMr); + + GenericInstanceType functionGenericInstance; + MethodReference functionConstructorInstanceMethodRef; + bool isAutoPacked = IsAutoPackedType(dataTypeRef); + + //Generate for autopacktype. + if (isAutoPacked) + { + functionGenericInstance = gh.FunctionT3TypeRef.MakeGenericInstanceType(ri.ReaderTypeRef, base.GetClass().AutoPackTypeRef, dataTypeRef); + functionConstructorInstanceMethodRef = gh.FunctionT3ConstructorMethodRef.MakeHostInstanceGeneric(base.Session, functionGenericInstance); + } + //Not autopacked. + else + { + functionGenericInstance = gh.FunctionT2TypeRef.MakeGenericInstanceType(ri.ReaderTypeRef, dataTypeRef); + functionConstructorInstanceMethodRef = gh.FunctionT2ConstructorMethodRef.MakeHostInstanceGeneric(base.Session, functionGenericInstance); + } + processor.Emit(OpCodes.Newobj, functionConstructorInstanceMethodRef); + + //Call delegate to GeneratedReader.Read + GenericInstanceType genericInstance = ri.GenericReaderTypeRef.MakeGenericInstanceType(dataTypeRef); + MethodReference genericReadMethodRef = (isAutoPacked) ? + ri.ReadAutoPackSetMethodRef.MakeHostInstanceGeneric(base.Session, genericInstance) : + ri.ReadSetMethodRef.MakeHostInstanceGeneric(base.Session, genericInstance); + processor.Emit(OpCodes.Call, genericReadMethodRef); + + processor.Emit(OpCodes.Ret); + } + + /// + /// Creates reader extension methods for built-in readers. + /// + private void CreateInstancedReaderExtensions() + { + if (!FishNetILPP.IsFishNetAssembly(base.Session)) + return; + + GeneralHelper gh = base.GetClass(); + ReaderProcessor gwh = base.GetClass(); + + //List staticReaders = new List(); + foreach (KeyValuePair item in InstancedReaderMethods) + { + MethodReference instancedReadMr = item.Value; + if (instancedReadMr.ContainsGenericParameter) + continue; + + TypeReference returnTr = base.ImportReference(instancedReadMr.ReturnType); + + MethodDefinition md = new MethodDefinition($"InstancedExtension___{instancedReadMr.Name}", + WriterProcessor.GENERATED_METHOD_ATTRIBUTES, + returnTr); + //Add extension parameter. + ParameterDefinition readerPd = gh.CreateParameter(md, typeof(Reader), "reader"); + //Add parameters needed by instanced writer. + List otherPds = md.CreateParameters(base.Session, instancedReadMr); + gh.MakeExtensionMethod(md); + // + gwh.GeneratedReaderClassTypeDef.Methods.Add(md); + + ILProcessor processor = md.Body.GetILProcessor(); + //Load writer. + processor.Emit(OpCodes.Ldarg, readerPd); + //Load args. + foreach (ParameterDefinition pd in otherPds) + processor.Emit(OpCodes.Ldarg, pd); + //Call instanced. + processor.Emit(instancedReadMr.GetCallOpCode(base.Session), instancedReadMr); + processor.Emit(OpCodes.Ret); + + AddReaderMethod(returnTr, md, false, true); + } + } + + /// + /// Removes typeRef from static/instanced reader methods. + /// + internal void RemoveReaderMethod(TypeReference typeRef, bool instanced) + { + string fullName = typeRef.GetFullnameWithoutBrackets(); + Dictionary dict = (instanced) ? + InstancedReaderMethods : StaticReaderMethods; + + dict.Remove(fullName); + } + + /// + /// Creates read instructions returning instructions and outputing variable of read result. + /// + internal List CreateRead(MethodDefinition methodDef, ParameterDefinition readerParameterDef, TypeReference readTypeRef, out VariableDefinition createdVariableDef) + { + ILProcessor processor = methodDef.Body.GetILProcessor(); + List insts = new List(); + MethodReference readMr = GetOrCreateReadMethodReference(readTypeRef); + if (readMr != null) + { + //Make a local variable. + createdVariableDef = base.GetClass().CreateVariable(methodDef, readTypeRef); + //pooledReader.ReadBool(); + insts.Add(processor.Create(OpCodes.Ldarg, readerParameterDef)); + //If an auto pack method then insert default value. + if (AutoPackedMethods.Contains(readTypeRef)) + { + AutoPackType packType = base.GetClass().GetDefaultAutoPackType(readTypeRef); + insts.Add(processor.Create(OpCodes.Ldc_I4, (int)packType)); + } + + + TypeReference valueTr = readTypeRef; + /* If generic then find write class for + * data type. Currently we only support one generic + * for this. */ + if (valueTr.IsGenericInstance) + { + GenericInstanceType git = (GenericInstanceType)valueTr; + TypeReference genericTr = git.GenericArguments[0]; + readMr = readMr.GetMethodReference(base.Session, genericTr); + } + + insts.Add(processor.Create(OpCodes.Call, readMr)); + //Store into local variable. + insts.Add(processor.Create(OpCodes.Stloc, createdVariableDef)); + return insts; + } + else + { + base.LogError("Reader not found for " + readTypeRef.ToString()); + createdVariableDef = null; + return null; + } + } + + + + /// + /// Creates a read for fieldRef and populates it into a created variable of class or struct type. + /// + internal bool CreateReadIntoClassOrStruct(MethodDefinition readerMd, ParameterDefinition readerPd, MethodReference readMr, VariableDefinition objectVd, FieldReference valueFr) + { + if (readMr != null) + { + ILProcessor processor = readerMd.Body.GetILProcessor(); + /* How to load object instance. If it's a structure + * then it must be loaded by address. Otherwise if + * class Ldloc can be used. */ + OpCode loadOpCode = (objectVd.VariableType.IsValueType) ? + OpCodes.Ldloca : OpCodes.Ldloc; + + /* If generic then find write class for + * data type. Currently we only support one generic + * for this. */ + if (valueFr.FieldType.IsGenericInstance) + { + GenericInstanceType git = (GenericInstanceType)valueFr.FieldType; + TypeReference genericTr = git.GenericArguments[0]; + readMr = readMr.GetMethodReference(base.Session, genericTr); + } + + processor.Emit(loadOpCode, objectVd); + //reader. + processor.Emit(OpCodes.Ldarg, readerPd); + if (IsAutoPackedType(valueFr.FieldType)) + { + AutoPackType packType = base.GetClass().GetDefaultAutoPackType(valueFr.FieldType); + processor.Emit(OpCodes.Ldc_I4, (int)packType); + } + //reader.ReadXXXX(). + processor.Emit(OpCodes.Call, readMr); + //obj.Field = result / reader.ReadXXXX(). + processor.Emit(OpCodes.Stfld, valueFr); + + return true; + } + else + { + base.LogError($"Reader not found for {valueFr.FullName}."); + return false; + } + } + + + /// + /// Creates a read for fieldRef and populates it into a created variable of class or struct type. + /// + internal bool CreateReadIntoClassOrStruct(MethodDefinition methodDef, ParameterDefinition readerPd, MethodReference readMr, VariableDefinition objectVariableDef, MethodReference setMr, TypeReference readTr) + { + if (readMr != null) + { + ILProcessor processor = methodDef.Body.GetILProcessor(); + + /* How to load object instance. If it's a structure + * then it must be loaded by address. Otherwise if + * class Ldloc can be used. */ + OpCode loadOpCode = (objectVariableDef.VariableType.IsValueType) ? + OpCodes.Ldloca : OpCodes.Ldloc; + + /* If generic then find write class for + * data type. Currently we only support one generic + * for this. */ + if (readTr.IsGenericInstance) + { + GenericInstanceType git = (GenericInstanceType)readTr; + TypeReference genericTr = git.GenericArguments[0]; + readMr = readMr.GetMethodReference(base.Session, genericTr); + } + + processor.Emit(loadOpCode, objectVariableDef); + //reader. + processor.Emit(OpCodes.Ldarg, readerPd); + if (IsAutoPackedType(readTr)) + { + AutoPackType packType = base.GetClass().GetDefaultAutoPackType(readTr); + processor.Emit(OpCodes.Ldc_I4, (int)packType); + } + //reader.ReadXXXX(). + processor.Emit(OpCodes.Call, readMr); + //obj.Property = result / reader.ReadXXXX(). + processor.Emit(OpCodes.Call, setMr); + + return true; + } + else + { + base.LogError($"Reader not found for {readTr.FullName}."); + return false; + } + } + + + + + /// + /// Creates generic write delegates for all currently known write types. + /// + internal void CreateStaticMethodDelegates() + { + foreach (KeyValuePair item in StaticReaderMethods) + base.GetClass().CreateReadDelegate(item.Value, true); + } + + + /// + /// Returns if typeRef has a deserializer. + /// + /// + /// + /// + internal bool HasDeserializer(TypeReference typeRef, bool createMissing) + { + bool result = (GetInstancedReadMethodReference(typeRef) != null) || + (GetStaticReadMethodReference(typeRef) != null); + + if (!result && createMissing) + { + if (!base.GetClass().HasNonSerializableAttribute(typeRef.CachedResolve(base.Session))) + { + MethodReference methodRef = CreateReader(typeRef); + result = (methodRef != null); + } + } + + return result; + } + + + /// + /// Returns if typeRef supports auto packing. + /// + /// + /// + internal bool IsAutoPackedType(TypeReference typeRef) + { + return AutoPackedMethods.Contains(typeRef); + } + /// + /// Creates a null check on the first argument and returns a null object if result indicates to do so. + /// + internal void CreateRetOnNull(ILProcessor processor, ParameterDefinition readerParameterDef, VariableDefinition resultVariableDef, bool useBool) + { + Instruction endIf = processor.Create(OpCodes.Nop); + + if (useBool) + CreateReadBool(processor, readerParameterDef, resultVariableDef); + else + CreateReadPackedWhole(processor, readerParameterDef, resultVariableDef); + + //If (true or == -1) jmp to endIf. True is null. + processor.Emit(OpCodes.Ldloc, resultVariableDef); + if (useBool) + { + processor.Emit(OpCodes.Brfalse, endIf); + } + else + { + //-1 + processor.Emit(OpCodes.Ldc_I4_M1); + processor.Emit(OpCodes.Bne_Un_S, endIf); + } + //Insert null. + processor.Emit(OpCodes.Ldnull); + //Exit method. + processor.Emit(OpCodes.Ret); + //End of if check. + processor.Append(endIf); + } + + /// + /// Creates a call to WriteBoolean with value. + /// + /// + /// + /// + internal void CreateReadBool(ILProcessor processor, ParameterDefinition readerParameterDef, VariableDefinition localBoolVariableDef) + { + MethodReference readBoolMethodRef = GetReadMethodReference(base.GetClass().GetTypeReference(typeof(bool))); + processor.Emit(OpCodes.Ldarg, readerParameterDef); + processor.Emit(readBoolMethodRef.GetCallOpCode(base.Session), readBoolMethodRef); + processor.Emit(OpCodes.Stloc, localBoolVariableDef); + } + + /// + /// Creates a call to WritePackWhole with value. + /// + /// + /// + internal void CreateReadPackedWhole(ILProcessor processor, ParameterDefinition readerParameterDef, VariableDefinition resultVariableDef) + { + //Reader. + processor.Emit(OpCodes.Ldarg, readerParameterDef); + //Reader.ReadPackedWhole(). + MethodReference readPwMr = base.GetClass().Reader_ReadPackedWhole_MethodRef; + processor.Emit(readPwMr.GetCallOpCode(base.Session), readPwMr); + processor.Emit(OpCodes.Conv_I4); + processor.Emit(OpCodes.Stloc, resultVariableDef); + } + + + #region GetReaderMethodReference. + /// + /// Returns the MethodReference for typeRef. + /// + /// + /// + internal MethodReference GetInstancedReadMethodReference(TypeReference typeRef) + { + string fullName = typeRef.GetFullnameWithoutBrackets(); + InstancedReaderMethods.TryGetValue(fullName, out MethodReference methodRef); + return methodRef; + } + /// + /// Returns the MethodReference for typeRef. + /// + /// + /// + internal MethodReference GetStaticReadMethodReference(TypeReference typeRef) + { + string fullName = typeRef.GetFullnameWithoutBrackets(); + StaticReaderMethods.TryGetValue(fullName, out MethodReference methodRef); + return methodRef; + } + /// + /// Returns the MethodReference for typeRef favoring instanced or static. Returns null if not found. + /// + /// + /// + /// + internal MethodReference GetReadMethodReference(TypeReference typeRef) + { + MethodReference result; + bool favorInstanced = false; + if (favorInstanced) + { + result = GetInstancedReadMethodReference(typeRef); + if (result == null) + result = GetStaticReadMethodReference(typeRef); + } + else + { + result = GetStaticReadMethodReference(typeRef); + if (result == null) + result = GetInstancedReadMethodReference(typeRef); + } + + return result; + } + /// + /// Returns the MethodReference for typeRef favoring instanced or static. + /// + /// + /// + /// + internal MethodReference GetOrCreateReadMethodReference(TypeReference typeRef) + { +#pragma warning disable CS0219 + bool favorInstanced = false; +#pragma warning restore CS0219 + //Try to get existing writer, if not present make one. + MethodReference readMethodRef = GetReadMethodReference(typeRef); + if (readMethodRef == null) + readMethodRef = CreateReader(typeRef); + + //If still null then return could not be generated. + if (readMethodRef == null) + { + base.LogError($"Could not create deserializer for {typeRef.FullName}."); + } + //Otherwise, check if generic and create writes for generic pararameters. + else if (typeRef.IsGenericInstance) + { + GenericInstanceType git = (GenericInstanceType)typeRef; + foreach (TypeReference item in git.GenericArguments) + { + MethodReference result = GetOrCreateReadMethodReference(item); + if (result == null) + { + base.LogError($"Could not create deserializer for {item.FullName}."); + return null; + } + } + } + + return readMethodRef; + } + #endregion + + + /// + /// Generates a reader for objectTypeReference if one does not already exist. + /// + /// + /// + internal MethodReference CreateReader(TypeReference objectTr) + { + MethodReference resultMr = null; + TypeDefinition objectTypeDef; + + SerializerType serializerType = base.GetClass().GetSerializerType(objectTr, false, out objectTypeDef); + if (serializerType != SerializerType.Invalid) + { + //Array. + if (serializerType == SerializerType.Array) + resultMr = CreateArrayReaderMethodReference(objectTr); + //Enum. + else if (serializerType == SerializerType.Enum) + resultMr = CreateEnumReaderMethodDefinition(objectTr); + else if (serializerType == SerializerType.Dictionary + || serializerType == SerializerType.List + || serializerType == SerializerType.ListCache) + resultMr = CreateGenericCollectionReaderMethodReference(objectTr, serializerType); + //NetworkBehaviour. + else if (serializerType == SerializerType.NetworkBehaviour) + resultMr = GetNetworkBehaviourReaderMethodReference(objectTr); + //Nullable. + else if (serializerType == SerializerType.Nullable) + resultMr = CreateNullableReaderMethodReference(objectTr); + //Class or struct. + else if (serializerType == SerializerType.ClassOrStruct) + resultMr = CreateClassOrStructReaderMethodReference(objectTr); + } + + //If was not created. + if (resultMr == null) + RemoveFromStaticReaders(objectTr); + + return resultMr; + } + + + /// + /// Removes from static writers. + /// + private void RemoveFromStaticReaders(TypeReference tr) + { + base.GetClass().RemoveReaderMethod(tr, false); + } + /// + /// Adds to static writers. + /// + private void AddToStaticReaders(TypeReference tr, MethodReference mr) + { + base.GetClass().AddReaderMethod(tr, mr.CachedResolve(base.Session), false, true); + } + + /// + /// Generates a reader for objectTypeReference if one does not already exist. + /// + /// + /// + private MethodReference CreateEnumReaderMethodDefinition(TypeReference objectTr) + { + MethodDefinition createdReaderMd = CreateStaticReaderStubMethodDefinition(objectTr); + AddToStaticReaders(objectTr, createdReaderMd); + + ILProcessor processor = createdReaderMd.Body.GetILProcessor(); + + //Get type reference for enum type. eg byte int + TypeReference underlyingTypeRef = objectTr.CachedResolve(base.Session).GetEnumUnderlyingTypeReference(); + //Get read method for underlying type. + MethodReference readMethodRef = base.GetClass().GetOrCreateReadMethodReference(underlyingTypeRef); + if (readMethodRef == null) + return null; + + ParameterDefinition readerParameterDef = createdReaderMd.Parameters[0]; + //reader.ReadXXX(). + processor.Emit(OpCodes.Ldarg, readerParameterDef); + if (base.GetClass().IsAutoPackedType(underlyingTypeRef)) + processor.Emit(OpCodes.Ldc_I4, (int)AutoPackType.Packed); + + processor.Emit(OpCodes.Call, readMethodRef); + + processor.Emit(OpCodes.Ret); + return base.ImportReference(createdReaderMd); + } + + + /// + /// Creates a read for a class type which inherits NetworkBehaviour. + /// + /// + /// + private MethodReference GetNetworkBehaviourReaderMethodReference(TypeReference objectTr) + { + MethodDefinition createdReaderMd = CreateStaticReaderStubMethodDefinition(objectTr); + AddToStaticReaders(objectTr, createdReaderMd); + + ILProcessor processor = createdReaderMd.Body.GetILProcessor(); + TypeReference networkBehaviourTypeRef = base.GetClass().GetTypeReference(typeof(NetworkBehaviour)); + + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Call, base.GetClass().GetReadMethodReference(networkBehaviourTypeRef)); + processor.Emit(OpCodes.Castclass, objectTr); + processor.Emit(OpCodes.Ret); + return base.ImportReference(createdReaderMd); + } + + /// + /// Creates a writer for an array. + /// + private MethodReference CreateArrayReaderMethodReference(TypeReference objectTr) + { + ReaderImports ri = base.GetClass(); + TypeReference valueTr = objectTr.GetElementType(); + + //Write not found. + if (GetOrCreateReadMethodReference(valueTr) == null) + return null; + + MethodDefinition createdMd = CreateStaticReaderStubMethodDefinition(objectTr); + AddToStaticReaders(objectTr, createdMd); + + //Find instanced writer to use. + MethodReference instancedWriteMr = ri.Reader_ReadArray_MethodRef; + //Make generic. + GenericInstanceMethod writeGim = instancedWriteMr.MakeGenericMethod(new TypeReference[] { valueTr }); + CallInstancedReader(createdMd, writeGim); + + return base.ImportReference(createdMd); + } + + + /// + /// Creates a reader for a dictionary. + /// + private MethodReference CreateDictionaryReaderMethodReference(TypeReference objectTr) + { + ReaderProcessor rp = base.GetClass(); + + GenericInstanceType genericInstance = (GenericInstanceType)objectTr; + base.ImportReference(genericInstance); + TypeReference keyTr = genericInstance.GenericArguments[0]; + TypeReference valueTr = genericInstance.GenericArguments[1]; + + /* Try to get instanced first for collection element type, if it doesn't exist then try to + * get/or make a one. */ + MethodReference keyWriteMr = rp.GetOrCreateReadMethodReference(keyTr); + MethodReference valueWriteMr = rp.GetOrCreateReadMethodReference(valueTr); + if (keyWriteMr == null || valueWriteMr == null) + return null; + + MethodDefinition createdReaderMd = CreateStaticReaderStubMethodDefinition(objectTr); + AddToStaticReaders(objectTr, createdReaderMd); + + GenericInstanceMethod readDictGim = base.GetClass().Reader_ReadDictionary_MethodRef.MakeGenericMethod(new TypeReference[] { keyTr, valueTr }); + CallInstancedReader(createdReaderMd, readDictGim); + + return base.ImportReference(createdReaderMd); + } + + + /// + /// Creates a writer for a variety of generic collections. + /// + private MethodReference CreateGenericCollectionReaderMethodReference(TypeReference objectTr, SerializerType st) + { + ReaderImports ri = base.GetClass(); + //Make value field generic. + GenericInstanceType genericInstance = (GenericInstanceType)objectTr; + base.ImportReference(genericInstance); + TypeReference valueTr = genericInstance.GenericArguments[0]; + + List genericArguments = new List(); + //Make sure all arguments have writers. + foreach (TypeReference gaTr in genericInstance.GenericArguments) + { + //Writer not found. + if (GetOrCreateReadMethodReference(gaTr) == null) + { + base.LogError($"Reader could not be found or created for type {gaTr.FullName}."); + return null; + } + genericArguments.Add(gaTr); + } + MethodReference valueWriteMr = GetOrCreateReadMethodReference(valueTr); + if (valueWriteMr == null) + return null; + + MethodDefinition createdMd = CreateStaticReaderStubMethodDefinition(objectTr); + AddToStaticReaders(objectTr, createdMd); + + //Find instanced writer to use. + MethodReference instancedReadMr; + if (st == SerializerType.Dictionary) + instancedReadMr = ri.Reader_ReadDictionary_MethodRef; + else if (st == SerializerType.List) + instancedReadMr = ri.Reader_ReadList_MethodRef; + else if (st == SerializerType.ListCache) + instancedReadMr = ri.Reader_ReadListCache_MethodRef; + else + instancedReadMr = null; + + //Not found. + if (instancedReadMr == null) + { + base.LogError($"Instanced reader not found for SerializerType {st} on object {objectTr.Name}."); + return null; + } + + //Make generic. + GenericInstanceMethod writeGim = instancedReadMr.MakeGenericMethod(genericArguments.ToArray()); + CallInstancedReader(createdMd, writeGim); + + return base.ImportReference(createdMd); + } + + + /// + /// Calls an instanced writer from a static writer. + /// + private void CallInstancedReader(MethodDefinition staticReaderMd, MethodReference instancedReaderMr) + { + ParameterDefinition readerPd = staticReaderMd.Parameters[0]; + ILProcessor processor = staticReaderMd.Body.GetILProcessor(); + processor.Emit(OpCodes.Ldarg, readerPd); + processor.Emit(instancedReaderMr.GetCallOpCode(base.Session), instancedReaderMr); + processor.Emit(OpCodes.Ret); + } + + ///// + ///// Create a reader for a list. + ///// + //private MethodReference CreateGenericTypeReader(TypeReference objectTr, SerializerType st) + //{ + // ReaderProcessor rp = base.GetClass(); + + // if (st != SerializerType.List && st != SerializerType.ListCache) + // { + // base.LogError($"Reader SerializerType {st} is not implemented"); + // return null; + // } + + // GenericInstanceType genericInstance = (GenericInstanceType)objectTr; + // base.ImportReference(genericInstance); + // TypeReference elementTr = genericInstance.GenericArguments[0]; + + // /* Try to get instanced first for collection element type, if it doesn't exist then try to + // * get/or make a one. */ + // MethodReference elementReadMr = rp.GetOrCreateReadMethodReference(elementTr); + // if (elementReadMr == null) + // return null; + + // TypeReference readerMethodTr = null; + // if (st == SerializerType.List) + // readerMethodTr = base.GetClass().GetTypeReference(typeof(List<>)); + // else if (st == SerializerType.ListCache) + // readerMethodTr = base.GetClass().GetTypeReference(typeof(ListCache<>)); + + // MethodReference readerMd = rp.GetReadMethodReference(readerMethodTr); + // MethodDefinition typedReaderMd = CreateStaticReaderStubMethodDefinition(objectTr); + + // AddToStaticReaders(objectTr, typedReaderMd); + + // ParameterDefinition readerPd = typedReaderMd.Parameters[0]; + + // //Find add method for list. + // MethodReference readerGim = readerMd.GetMethodReference(base.Session, elementTr); + // ILProcessor processor = readerMd.CachedResolve(base.Session).Body.GetILProcessor(); + // processor.Emit(OpCodes.Ldarg, readerPd); + // processor.Emit(OpCodes.Call, readerGim); + + // return elementReadMr; + //} + + + /// + /// Creates a reader method for a struct or class objectTypeRef. + /// + /// + /// + private MethodReference CreateNullableReaderMethodReference(TypeReference objectTr) + { + ReaderProcessor rp = base.GetClass(); + + GenericInstanceType objectGit = objectTr as GenericInstanceType; + TypeReference valueTr = objectGit.GenericArguments[0]; + + //Make sure object has a ctor. + MethodDefinition objectCtorMd = objectTr.GetConstructor(base.Session, 1); + if (objectCtorMd == null) + { + base.LogError($"{objectTr.Name} can't be deserialized because the nullable type does not have a constructor."); + return null; + } + + //Get the reader for the value. + MethodReference valueReaderMr = rp.GetOrCreateReadMethodReference(valueTr); + if (valueReaderMr == null) + return null; + + TypeDefinition objectTd = objectTr.CachedResolve(base.Session); + MethodDefinition createdReaderMd = CreateStaticReaderStubMethodDefinition(objectTr); + AddToStaticReaders(objectTr, createdReaderMd); + + ILProcessor processor = createdReaderMd.Body.GetILProcessor(); + + ParameterDefinition readerPd = createdReaderMd.Parameters[0]; + // create local for return value + VariableDefinition resultVd = base.GetClass().CreateVariable(createdReaderMd, objectTr); + + //Read if null into boolean. + VariableDefinition nullBoolVd = createdReaderMd.CreateVariable(base.Session, typeof(bool)); + rp.CreateReadBool(processor, readerPd, nullBoolVd); + + Instruction afterReturnNullInst = processor.Create(OpCodes.Nop); + processor.Emit(OpCodes.Ldloc, nullBoolVd); + processor.Emit(OpCodes.Brfalse, afterReturnNullInst); + //Return a null result. + base.GetClass().SetVariableDefinitionFromObject(processor, resultVd, objectTd); + processor.Emit(OpCodes.Ldloc, resultVd); + processor.Emit(OpCodes.Ret); + processor.Append(afterReturnNullInst); + + MethodReference initMr = objectCtorMd.MakeHostInstanceGeneric(base.Session, objectGit); + processor.Emit(OpCodes.Ldarg, readerPd); + //If an auto pack method then insert default value. + if (rp.IsAutoPackedType(valueTr)) + { + AutoPackType packType = base.GetClass().GetDefaultAutoPackType(valueTr); + processor.Emit(OpCodes.Ldc_I4, (int)packType); + } + processor.Emit(OpCodes.Call, valueReaderMr); + processor.Emit(OpCodes.Newobj, initMr); + processor.Emit(OpCodes.Ret); + + return base.ImportReference(createdReaderMd); + } + + + /// + /// Creates a reader method for a struct or class objectTypeRef. + /// + /// + /// + private MethodReference CreateClassOrStructReaderMethodReference(TypeReference objectTr) + { + MethodDefinition createdReaderMd = CreateStaticReaderStubMethodDefinition(objectTr); + AddToStaticReaders(objectTr, createdReaderMd); + + TypeDefinition objectTypeDef = objectTr.CachedResolve(base.Session); + ILProcessor processor = createdReaderMd.Body.GetILProcessor(); + + ParameterDefinition readerParameterDef = createdReaderMd.Parameters[0]; + // create local for return value + VariableDefinition objectVariableDef = base.GetClass().CreateVariable(createdReaderMd, objectTr); + + //If not a value type create a return null check. + if (!objectTypeDef.IsValueType) + { + VariableDefinition nullVariableDef = base.GetClass().CreateVariable(createdReaderMd, typeof(bool)); + //Load packed whole value into sizeVariableDef, exit if null indicator. + base.GetClass().CreateRetOnNull(processor, readerParameterDef, nullVariableDef, true); + } + + /* If here then not null. */ + //Make a new instance of object type and set to objectVariableDef. + base.GetClass().SetVariableDefinitionFromObject(processor, objectVariableDef, objectTypeDef); + if (!ReadFieldsAndProperties(createdReaderMd, readerParameterDef, objectVariableDef, objectTr)) + return null; + /* //codegen scriptableobjects seem to climb too high up to UnityEngine.Object when + * creating serializers/deserialized. Make sure this is not possible. */ + + //Load result and return it. + processor.Emit(OpCodes.Ldloc, objectVariableDef); + processor.Emit(OpCodes.Ret); + + return base.ImportReference(createdReaderMd); + } + + + /// + /// Reads all fields of objectTypeRef. + /// + private bool ReadFieldsAndProperties(MethodDefinition readerMd, ParameterDefinition readerPd, VariableDefinition objectVd, TypeReference objectTr) + { + ReaderProcessor rp = base.GetClass(); + + //This probably isn't needed but I'm too afraid to remove it. + if (objectTr.Module != base.Module) + objectTr = base.ImportReference(objectTr.CachedResolve(base.Session)); + + //Fields. + foreach (FieldDefinition fieldDef in objectTr.FindAllSerializableFields(base.Session + , ReaderProcessor.EXCLUDED_AUTO_SERIALIZER_TYPES, ReaderProcessor.EXCLUDED_ASSEMBLY_PREFIXES)) + { + FieldReference importedFr = base.ImportReference(fieldDef); + if (GetReadMethod(fieldDef.FieldType, out MethodReference readMr)) + rp.CreateReadIntoClassOrStruct(readerMd, readerPd, readMr, objectVd, importedFr); + } + + //Properties. + foreach (PropertyDefinition propertyDef in objectTr.FindAllSerializableProperties(base.Session + , ReaderProcessor.EXCLUDED_AUTO_SERIALIZER_TYPES, ReaderProcessor.EXCLUDED_ASSEMBLY_PREFIXES)) + { + if (GetReadMethod(propertyDef.PropertyType, out MethodReference readMr)) + { + MethodReference setMr = base.Module.ImportReference(propertyDef.SetMethod); + rp.CreateReadIntoClassOrStruct(readerMd, readerPd, readMr, objectVd, setMr, propertyDef.PropertyType); + } + } + + //Gets or creates writer method and outputs it. Returns true if method is found or created. + bool GetReadMethod(TypeReference tr, out MethodReference readMr) + { + tr = base.ImportReference(tr); + readMr = rp.GetOrCreateReadMethodReference(tr); + return (readMr != null); + } + + return true; + } + + + /// + /// Creates the stub for a new reader method. + /// + /// + /// + public MethodDefinition CreateStaticReaderStubMethodDefinition(TypeReference objectTypeRef, string nameExtension = WriterProcessor.GENERATED_WRITER_NAMESPACE) + { + string methodName = $"{GENERATED_READ_PREFIX}{objectTypeRef.FullName}{nameExtension}s"; + // create new reader for this type + TypeDefinition readerTypeDef = base.GetClass().GetOrCreateClass(out _, GENERATED_TYPE_ATTRIBUTES, GENERATED_READERS_CLASS_NAME, null); + MethodDefinition readerMethodDef = readerTypeDef.AddMethod(methodName, + ReaderProcessor.GENERATED_METHOD_ATTRIBUTES, + objectTypeRef); + + base.GetClass().CreateParameter(readerMethodDef, base.GetClass().Reader_TypeRef, "reader"); + readerMethodDef.Body.InitLocals = true; + + return readerMethodDef; + } + + + } +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Processing/ReaderProcessor.cs.meta b/Assets/FishNet/CodeGenerating/Processing/ReaderProcessor.cs.meta new file mode 100644 index 0000000..d041ead --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/ReaderProcessor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0f9d3654f5816c4409b88fa0602c6df4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Processing/Rpc.meta b/Assets/FishNet/CodeGenerating/Processing/Rpc.meta new file mode 100644 index 0000000..a1e3948 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Rpc.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: de5f90c539e844445be428ff2a2fdf29 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Processing/Rpc/AttributeData.cs b/Assets/FishNet/CodeGenerating/Processing/Rpc/AttributeData.cs new file mode 100644 index 0000000..3d69a01 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Rpc/AttributeData.cs @@ -0,0 +1,68 @@ +using FishNet.CodeGenerating.Helping; +using FishNet.Object.Helping; +using MonoFN.Cecil; +using System.Collections.Generic; + +namespace FishNet.CodeGenerating.Processing.Rpc +{ + internal static class AttributeDataExtensions + { + + /// + /// Returns RpcTypes in datas. + /// + public static List GetRpcTypes(this List datas) + { + //RpcTypes for originalMd. + List rpcTypes = new List(); + foreach (AttributeData ad in datas) + rpcTypes.Add(ad.RpcType); + + return rpcTypes; + } + + /// + /// Gets CustomAttribute for rpcType + /// + public static CustomAttribute GetAttribute(this List datas, CodegenSession session, RpcType rpcType) + { + for (int i = 0; i < datas.Count; i++) + { + if (datas[i].RpcType == rpcType) + return datas[i].Attribute; + } + + session.LogError($"RpcType {rpcType} not found in datas."); + return null; + } + + + /// + /// Returns RpcType as flag through combining datas. + /// + /// + /// + public static RpcType GetCombinedRpcType(this List datas) + { + RpcType result = RpcType.None; + for (int i = 0; i < datas.Count; i++) + result |= datas[i].RpcType; + + return result; + } + } + + internal class AttributeData + { + public readonly CustomAttribute Attribute; + public readonly RpcType RpcType; + + public AttributeData(CustomAttribute attribute, RpcType rpcType) + { + Attribute = attribute; + RpcType = rpcType; + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Processing/Rpc/AttributeData.cs.meta b/Assets/FishNet/CodeGenerating/Processing/Rpc/AttributeData.cs.meta new file mode 100644 index 0000000..69cbe34 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Rpc/AttributeData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 91f84e00db3d1ad448fb6a760afb6927 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Processing/Rpc/Attributes.cs b/Assets/FishNet/CodeGenerating/Processing/Rpc/Attributes.cs new file mode 100644 index 0000000..dbeb798 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Rpc/Attributes.cs @@ -0,0 +1,166 @@ +using FishNet.CodeGenerating.Helping; +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.Connection; +using FishNet.Object.Helping; +using MonoFN.Cecil; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace FishNet.CodeGenerating.Processing.Rpc +{ + internal class Attributes : CodegenBase + { + + /// + /// Returns if methodDef has any Rpc attribute. + /// + public bool HasRpcAttributes(MethodDefinition methodDef) + { + foreach (CustomAttribute customAttribute in methodDef.CustomAttributes) + { + RpcType rt = base.Session.GetClass().GetRpcAttributeType(customAttribute); + if (rt != RpcType.None) + return true; + } + + //Fall through, nothing found. + return false; + } + + /// + /// Returns a collection of RpcAttribute for methodDef. + /// + public List GetRpcAttributes(MethodDefinition methodDef) + { + List results = new List(); + string asyncAttributeFullName = typeof(AsyncStateMachineAttribute).FullName; + bool isAsync = false; + + foreach (CustomAttribute customAttribute in methodDef.CustomAttributes) + { + RpcType rt = base.Session.GetClass().GetRpcAttributeType(customAttribute); + if (rt != RpcType.None) + { + results.Add(new AttributeData(customAttribute, rt)); + } + //Not a rpc attribute. + else + { + //Check if async. + if (customAttribute.Is(asyncAttributeFullName)) + isAsync = true; + } + } + + //Nothing found, exit early. + if (results.Count == 0) + { + return results; + } + //If has at least one RPC attrivbute and is an async method. + else if (isAsync) + { + base.Session.LogError($"{methodDef.Name} is an async RPC. This feature is not currently supported. You may instead run an async method from this RPC."); + return new List(); + } + //If more than one attribute make sure the combination is allowed. + else if (results.Count >= 2) + { + RpcType allRpcTypes = results.GetCombinedRpcType(); + if (allRpcTypes != (RpcType.Observers | RpcType.Target)) + { + base.Session.LogError($"{methodDef.Name} contains multiple RPC attributes. Only ObserversRpc and TargetRpc attributes may be combined."); + return new List(); + } + } + + //Next validate that the method is setup properly for each rpcType. + foreach (AttributeData ad in results) + { + //If not valid then return empty list. + if (!IsRpcMethodValid(methodDef, ad.RpcType)) + return new List(); + } + + return results; + } + + /// + /// Returns if a RpcMethod can be serialized and has a proper signature. + /// + private bool IsRpcMethodValid(MethodDefinition methodDef, RpcType rpcType) + { + //Static method. + if (methodDef.IsStatic) + { + base.Session.LogError($"{methodDef.Name} RPC method cannot be static."); + return false; + } + //Is generic type. + else if (methodDef.HasGenericParameters) + { + base.Session.LogError($"{methodDef.Name} RPC method cannot contain generic parameters."); + return false; + } + //Abstract method. + else if (methodDef.IsAbstract) + { + base.Session.LogError($"{methodDef.Name} RPC method cannot be abstract."); + return false; + } + //Non void return. + else if (methodDef.ReturnType != methodDef.Module.TypeSystem.Void) + { + base.Session.LogError($"{methodDef.Name} RPC method must return void."); + return false; + } + //Misc failing conditions. + else + { + //Check for async attribute. + foreach (CustomAttribute ca in methodDef.CustomAttributes) + { + + } + } + //TargetRpc but missing correct parameters. + if (rpcType == RpcType.Target) + { + if (methodDef.Parameters.Count == 0 || !methodDef.Parameters[0].Is(typeof(NetworkConnection))) + { + base.Session.LogError($"Target RPC {methodDef.Name} must have a NetworkConnection as the first parameter."); + return false; + } + } + + //Make sure all parameters can be serialized. + for (int i = 0; i < methodDef.Parameters.Count; i++) + { + ParameterDefinition parameterDef = methodDef.Parameters[i]; + + //If NetworkConnection, TargetRpc, and first parameter. + if ((i == 0) && (rpcType == RpcType.Target) && parameterDef.Is(typeof(NetworkConnection))) + continue; + + if (parameterDef.ParameterType.IsGenericParameter) + { + base.Session.LogError($"RPC method{methodDef.Name} contains a generic parameter. This is currently not supported."); + return false; + } + + //Can be serialized/deserialized. + bool canSerialize = base.GetClass().HasSerializerAndDeserializer(parameterDef.ParameterType, true); + if (!canSerialize) + { + base.Session.LogError($"RPC method {methodDef.Name} parameter type {parameterDef.ParameterType.FullName} does not support serialization. Use a supported type or create a custom serializer."); + return false; + } + + } + + //Fall through, success. + return true; + } + + } +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Processing/Rpc/Attributes.cs.meta b/Assets/FishNet/CodeGenerating/Processing/Rpc/Attributes.cs.meta new file mode 100644 index 0000000..e0ab4ab --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Rpc/Attributes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 974ebf09757267941a86f92e5072258c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Processing/Rpc/CreatedRpc.cs b/Assets/FishNet/CodeGenerating/Processing/Rpc/CreatedRpc.cs new file mode 100644 index 0000000..c2bd411 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Rpc/CreatedRpc.cs @@ -0,0 +1,58 @@ +using FishNet.Object.Helping; +using MonoFN.Cecil; +using System.Collections.Generic; + +namespace FishNet.CodeGenerating.Processing.Rpc +{ + + internal class CreatedRpc + { + public MethodDefinition OriginalMethodDef; + public uint MethodHash; + public AttributeData AttributeData; + public MethodDefinition WriterMethodDef; + public MethodDefinition ReaderMethodDef; + public MethodDefinition LogicMethodDef; + public MethodDefinition RedirectMethodDef; + public bool RunLocally; + + public RpcType RpcType => AttributeData.RpcType; + public CustomAttribute Attribute => AttributeData.Attribute; + public TypeDefinition TypeDef => OriginalMethodDef.DeclaringType; + public ModuleDefinition Module => OriginalMethodDef.Module; + } + + + internal static class CreatedRpcExtensions + { + /// + /// Returns CreatedRpc for rpcType. + /// + /// + public static CreatedRpc GetCreatedRpc(this List lst, RpcType rpcType) + { + for (int i = 0; i < lst.Count; i++) + { + if (lst[i].RpcType == rpcType) + return lst[i]; + } + //Fall through. + return null; + } + + /// + /// Returns combined RpcType for all entries. + /// + /// + public static RpcType GetCombinedRpcType(this List lst) + { + RpcType result = RpcType.None; + for (int i = 0; i < lst.Count; i++) + result |= lst[i].RpcType; + + return result; + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Processing/Rpc/CreatedRpc.cs.meta b/Assets/FishNet/CodeGenerating/Processing/Rpc/CreatedRpc.cs.meta new file mode 100644 index 0000000..bc68749 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Rpc/CreatedRpc.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c2176b6bfcc49934d8f36fba3df74d0c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Processing/Rpc/RpcProcessor.cs b/Assets/FishNet/CodeGenerating/Processing/Rpc/RpcProcessor.cs new file mode 100644 index 0000000..4f2bd28 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Rpc/RpcProcessor.cs @@ -0,0 +1,1084 @@ +using FishNet.Utility.Extension; +using FishNet.CodeGenerating.Extension; +using FishNet.CodeGenerating.Helping; +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.Configuring; +using FishNet.Connection; +using FishNet.Managing.Logging; +using FishNet.Object.Helping; +using FishNet.Transporting; +using MonoFN.Cecil; +using MonoFN.Cecil.Cil; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; + +namespace FishNet.CodeGenerating.Processing.Rpc +{ + internal class RpcProcessor : CodegenBase + { + + #region Types. + private struct DelegateData + { + public RpcType RpcType; + public bool RunLocally; + public MethodDefinition OriginalMethodDef; + public MethodDefinition ReaderMethodDef; + public uint MethodHash; + public CustomAttribute RpcAttribute; + + public DelegateData(RpcType rpcType, bool runLocally, MethodDefinition originalMethodDef, MethodDefinition readerMethodDef, uint methodHash, CustomAttribute rpcAttribute) + { + RpcType = rpcType; + RunLocally = runLocally; + OriginalMethodDef = originalMethodDef; + ReaderMethodDef = readerMethodDef; + MethodHash = methodHash; + RpcAttribute = rpcAttribute; + } + } + + #endregion + + #region Public. + /// + /// Attribute helper. + /// + public Attributes Attributes = new Attributes(); + #endregion + + private List<(MethodDefinition, MethodDefinition)> _virtualRpcs = new List<(MethodDefinition createdLogicMd, MethodDefinition originalRpcMd)>(); + + #region Const. + private const string LOGIC_PREFIX = "RpcLogic___"; + private const string WRITER_PREFIX = "RpcWriter___"; + private const string READER_PREFIX = "RpcReader___"; + private const string REQUIREOWNERSHIP_NAME = "RequireOwnership"; + private const string RUNLOCALLY_NAME = "RunLocally"; + private const string EXCLUDEOWNER_NAME = "ExcludeOwner"; + private const string EXCLUDESERVER_NAME = "ExcludeServer"; + private const string BUFFERLAST_NAME = "BufferLast"; + private const string DATALENGTH_NAME = "DataLength"; + private const string VALIDATETARGET_NAME = "ValidateTarget"; + #endregion + + public override bool ImportReferences() + { + Attributes.Initialize(base.Session); + return base.ImportReferences(); + } + + internal bool ProcessLocal(TypeDefinition typeDef, ref uint rpcCount) + { + bool modified = false; + + //All createdRpcs for typeDef. + List typeDefCeatedRpcs = new List(); + List methodDefs = typeDef.Methods.ToList(); + foreach (MethodDefinition md in methodDefs) + { + if (rpcCount >= NetworkBehaviourHelper.MAX_RPC_ALLOWANCE) + { + base.LogError($"{typeDef.FullName} and inherited types exceed {NetworkBehaviourHelper.MAX_RPC_ALLOWANCE} RPC methods. Only {NetworkBehaviourHelper.MAX_RPC_ALLOWANCE} RPC methods are supported per inheritance hierarchy."); + return false; + } + + //Rpcs created for this method. + List createdRpcs = new List(); + List attributeDatas = Attributes.GetRpcAttributes(md); + bool success = true; + foreach (AttributeData ad in attributeDatas) + { + CreatedRpc cr = new CreatedRpc(); + cr.OriginalMethodDef = md; + cr.AttributeData = ad; + cr.MethodHash = rpcCount; + + /* This is a one time check to make sure the rpcType is + * a supported value. Multiple methods beyond this rely on the + * value being supported. Rather than check in each method a + * single check is performed here. */ + if (cr.RpcType != RpcType.Observers && cr.RpcType != RpcType.Server && cr.RpcType != RpcType.Target) + { + base.LogError($"RpcType of {cr.RpcType.ToString()} is unhandled."); + break; + } + + bool created = CreateRpcMethods(attributeDatas, cr); + if (created) + { + modified = true; + + typeDefCeatedRpcs.Add(cr); + createdRpcs.Add(cr); + + if (cr.LogicMethodDef != null && cr.LogicMethodDef.IsVirtual) + _virtualRpcs.Add((cr.LogicMethodDef, md)); + + rpcCount++; + } + else + { + success = false; + } + } + + //If at least one attribute was found and all rpc methods were made. + if (createdRpcs.Count > 0 && success) + RedirectOriginalToWriter(createdRpcs); + } + + if (modified) + { + foreach (CreatedRpc cr in typeDefCeatedRpcs) + { + base.GetClass().CreateRpcDelegate(cr.RunLocally, cr.TypeDef, + cr.ReaderMethodDef, cr.RpcType, cr.MethodHash, + cr.Attribute); + } + return true; + } + else + { + return false; + } + } + + /// + /// Returns the name to use for a RpcMethod. + /// + private string GetRpcMethodName(CreatedRpc cr) + { + return GetRpcMethodName(cr.RpcType, cr.OriginalMethodDef); + } + + /// + /// Returns the name to use for a RpcMethod. + /// + private string GetRpcMethodName(RpcType rpcType, MethodDefinition originalMd) + { + return $"{rpcType}_{GetMethodNameAsParameters(originalMd)}"; + } + + /// + /// Returns the method name with parameter types included within the name. + /// + public static string GetMethodNameAsParameters(MethodDefinition methodDef) + { + StringBuilder sb = new StringBuilder(); + foreach (ParameterDefinition pd in methodDef.Parameters) + sb.Append(pd.ParameterType.FullName); + + return $"{methodDef.Name}_{sb.ToString().GetStableHash32()}"; + } + + /// + /// Redirects base calls for overriden RPCs. + /// + internal void RedirectBaseCalls() + { + foreach ((MethodDefinition logicMd, MethodDefinition originalMd) in _virtualRpcs) + RedirectBaseCall(logicMd, originalMd); + } + + /// + /// Gets number of RPCs by checking for RPC attributes. This does not perform error checking. + /// + /// + /// + internal uint GetRpcCount(TypeDefinition typeDef) + { + uint count = 0; + foreach (MethodDefinition methodDef in typeDef.Methods) + { + foreach (CustomAttribute customAttribute in methodDef.CustomAttributes) + { + RpcType rpcType = base.GetClass().GetRpcAttributeType(customAttribute); + if (rpcType != RpcType.None) + { + count++; + break; + } + } + } + + return count; + } + + /// + /// Creates all methods needed for a RPC. + /// + /// + /// + /// True if successful. + private bool CreateRpcMethods(List datas, CreatedRpc cr) + { + cr.RunLocally = cr.Attribute.GetField(RUNLOCALLY_NAME, false); + bool intentionallyNull; + + List serializedParameters = GetSerializedParamters(cr.RpcType, datas, cr); + + cr.WriterMethodDef = CreateRpcWriterMethod(serializedParameters, datas, cr, out intentionallyNull); + if (!intentionallyNull && cr.WriterMethodDef == null) + return false; + + cr.LogicMethodDef = CreateRpcLogicMethod(datas, cr, out intentionallyNull); + if (!intentionallyNull && cr.LogicMethodDef == null) + return false; + + cr.ReaderMethodDef = CreateRpcReaderMethod(serializedParameters, datas, cr, out intentionallyNull); + if (!intentionallyNull && cr.ReaderMethodDef == null) + return false; + + + return true; + } + + + + /// + /// Creates a writer for a RPC. + /// + /// + /// + /// + private MethodDefinition CreateRpcWriterMethod(List serializedParameters, List datas, CreatedRpc cr, out bool intentionallyNull) + { + intentionallyNull = false; + + + + string methodName = $"{WRITER_PREFIX}{GetRpcMethodName(cr)}"; + /* If method already exist then clear it. This + * can occur when a method needs to be rebuilt due to + * inheritence, and renumbering the RPC method names. */ + MethodDefinition createdMd = cr.TypeDef.GetMethod(methodName); + //If found. + if (createdMd != null) + { + createdMd.Parameters.Clear(); + createdMd.Body.Instructions.Clear(); + } + //Doesn't exist, create it. + else + { + //Create the method body. + createdMd = new MethodDefinition(methodName, + MethodAttributes.Private, + cr.Module.TypeSystem.Void); + cr.TypeDef.Methods.Add(createdMd); + createdMd.Body.InitLocals = true; + } + cr.WriterMethodDef = createdMd; + + bool result; + if (cr.RpcType == RpcType.Server) + result = CreateServerRpcWriterMethod(serializedParameters, cr); + else if (cr.RpcType == RpcType.Target || cr.RpcType == RpcType.Observers) + result = CreateClientRpcWriterMethod(serializedParameters, datas, cr); + else + result = false; + + return (result) ? cr.WriterMethodDef : null; + } + + /// + /// Returns serializable parameters for originalMd. + /// + private List GetSerializedParamters(RpcType rpcType, List attributeDatas, CreatedRpc cr) + { + MethodDefinition originalMd = cr.OriginalMethodDef; + + //RpcTypes for originalMd. + List attributeRpcTypes = attributeDatas.GetRpcTypes(); + + //Parameters to be serialized. + List serializedParameters = new List(); + /* Parameters which won't be serialized, such as channel. + * It's safe to add parameters which are null or + * not used. */ + HashSet nonserializedParameters = new HashSet(); + + //Get channel if it exist, and get target parameter. + ParameterDefinition channelParameterDef = GetChannelParameter(originalMd, rpcType); + + /* RpcType specific parameters. */ + ParameterDefinition targetConnectionParameterDef = null; + if (attributeRpcTypes.Contains(RpcType.Target)) + targetConnectionParameterDef = originalMd.Parameters[0]; + + if (rpcType == RpcType.Server) + { + //The network connection parameter might be added as null, this is okay. + nonserializedParameters.Add(GetNetworkConnectionParameter(originalMd)); + nonserializedParameters.Add(channelParameterDef); + } + else + { + nonserializedParameters.Add(channelParameterDef); + nonserializedParameters.Add(targetConnectionParameterDef); + } + + //Add all parameters which are NOT nonserialized to serializedParameters. + foreach (ParameterDefinition pd in originalMd.Parameters) + { + if (!nonserializedParameters.Contains(pd)) + serializedParameters.Add(pd); + } + + return serializedParameters; + } + + /// + /// Creates Writer method for a TargetRpc. + /// + private bool CreateClientRpcWriterMethod(List serializedParameters, List attributeDatas, CreatedRpc cr) + { + WriterProcessor wp = base.GetClass(); + + MethodDefinition writerMd = cr.WriterMethodDef; + MethodDefinition originalMd = cr.OriginalMethodDef; + + ILProcessor processor = writerMd.Body.GetILProcessor(); + //Add all parameters from the original. + for (int i = 0; i < originalMd.Parameters.Count; i++) + writerMd.Parameters.Add(originalMd.Parameters[i]); + //Get channel if it exist, and get target parameter. + ParameterDefinition channelParameterDef = GetChannelParameter(writerMd, RpcType.None); + + List rpcTypes = attributeDatas.GetRpcTypes(); + + /* RpcType specific parameters. */ + ParameterDefinition targetConnectionParameterDef = null; + if (rpcTypes.Contains(RpcType.Target)) + targetConnectionParameterDef = writerMd.Parameters[0]; + + /* Creates basic ServerRpc and ClientRpc + * conditions such as if requireOwnership ect.. + * or if (!base.isClient) */ + + CreateClientRpcConditionsForServer(writerMd); + + VariableDefinition channelVariableDef = CreateAndPopulateChannelVariable(writerMd, channelParameterDef); + /* Create a local PooledWriter variable. */ + //Default value for data lenght. + int dataLength = -1; + //Go through each attribute and see if a larger data length is specified. + foreach (AttributeData ad in attributeDatas) + { + int dl = ad.Attribute.GetField(DATALENGTH_NAME, -1); + if (dl > dataLength) + dataLength = dl; + } + VariableDefinition pooledWriterVariableDef = wp.CreatePooledWriter(writerMd, dataLength); + //Create all writer.WriteType() calls. + for (int i = 0; i < serializedParameters.Count; i++) + { + MethodReference writeMethodRef = wp.GetOrCreateWriteMethodReference(serializedParameters[i].ParameterType); + if (writeMethodRef == null) + return false; + + wp.CreateWrite(writerMd, pooledWriterVariableDef, serializedParameters[i], writeMethodRef); + } + + /* Call the method on NetworkBehaviour responsible for sending out the rpc. */ + if (cr.RpcType == RpcType.Observers) + processor.Add(CreateSendObserversRpc(writerMd, cr.MethodHash, pooledWriterVariableDef, channelVariableDef, cr.Attribute)); + else if (cr.RpcType == RpcType.Target) + processor.Add(CreateSendTargetRpc(writerMd, cr.MethodHash, pooledWriterVariableDef, channelVariableDef, targetConnectionParameterDef, attributeDatas)); + //Dispose of writer. + processor.Add(base.GetClass().DisposePooledWriter(writerMd, pooledWriterVariableDef)); + //Add end of method. + processor.Emit(OpCodes.Ret); + + return true; + } + + /// + /// Creates Writer method for a ServerRpc. + /// + private bool CreateServerRpcWriterMethod(List serializedParameters, CreatedRpc cr) + { + WriterProcessor wp = base.GetClass(); + + MethodDefinition writerMd = cr.WriterMethodDef; + MethodDefinition originalMd = cr.OriginalMethodDef; + ILProcessor processor = writerMd.Body.GetILProcessor(); + + //Add all parameters from the original. + for (int i = 0; i < originalMd.Parameters.Count; i++) + writerMd.Parameters.Add(originalMd.Parameters[i]); + //Add in channel if it doesnt exist. + ParameterDefinition channelParameterDef = GetChannelParameter(writerMd, RpcType.Server); + + /* Creates basic ServerRpc + * conditions such as if requireOwnership ect.. + * or if (!base.isClient) */ + + CreateServerRpcConditionsForClient(writerMd, cr.Attribute); + + VariableDefinition channelVariableDef = CreateAndPopulateChannelVariable(writerMd, channelParameterDef); + //Create a local PooledWriter variable. + int dataLength = cr.Attribute.GetField(DATALENGTH_NAME, -1); + VariableDefinition pooledWriterVariableDef = wp.CreatePooledWriter(writerMd, dataLength); + //Create all writer.WriteType() calls. + for (int i = 0; i < serializedParameters.Count; i++) + { + MethodReference writeMethodRef = wp.GetOrCreateWriteMethodReference(serializedParameters[i].ParameterType); + if (writeMethodRef == null) + return false; + + wp.CreateWrite(writerMd, pooledWriterVariableDef, serializedParameters[i], writeMethodRef); + } + + //Call the method on NetworkBehaviour responsible for sending out the rpc. + processor.Add(CreateSendServerRpc(writerMd, cr.MethodHash, pooledWriterVariableDef, channelVariableDef)); + //Dispose of writer. + processor.Add(wp.DisposePooledWriter(writerMd, pooledWriterVariableDef)); + //Add end of method. + processor.Emit(OpCodes.Ret); + + return true; + } + + /// + /// Creates a Channel VariableDefinition and populates it with parameterDef value if available, otherwise uses Channel.Reliable. + /// + /// + /// + /// + private VariableDefinition CreateAndPopulateChannelVariable(MethodDefinition methodDef, ParameterDefinition parameterDef) + { + ILProcessor processor = methodDef.Body.GetILProcessor(); + + VariableDefinition localChannelVariableDef = base.GetClass().CreateVariable(methodDef, typeof(Channel)); + if (parameterDef != null) + processor.Emit(OpCodes.Ldarg, parameterDef); + else + processor.Emit(OpCodes.Ldc_I4, (int)Channel.Reliable); + + //Set to local value. + processor.Emit(OpCodes.Stloc, localChannelVariableDef); + return localChannelVariableDef; + } + + + /// + /// Creates a reader for a RPC. + /// + /// + /// + /// + private MethodDefinition CreateRpcReaderMethod(List serializedParameters, List datas, CreatedRpc cr, out bool intentionallyNull) + { + intentionallyNull = false; + + RpcType rpcType = cr.RpcType; + MethodDefinition originalMd = cr.OriginalMethodDef; + TypeDefinition typeDef = cr.TypeDef; + bool runLocally = cr.RunLocally; + MethodDefinition logicMd = cr.LogicMethodDef; + CustomAttribute rpcAttribute = cr.Attribute; + + + + string methodName = $"{READER_PREFIX}{GetRpcMethodName(cr)}"; + /* If method already exist then just return it. This + * can occur when a method needs to be rebuilt due to + * inheritence, and renumbering the RPC method names. + * The reader method however does not need to be rewritten. */ + MethodDefinition createdMd = typeDef.GetMethod(methodName); + //If found. + if (createdMd != null) + { + cr.ReaderMethodDef = createdMd; + return createdMd; + } + else + { + //Create the method body. + createdMd = new MethodDefinition( + methodName, + MethodAttributes.Private, + originalMd.Module.TypeSystem.Void); + typeDef.Methods.Add(createdMd); + createdMd.Body.InitLocals = true; + cr.ReaderMethodDef = createdMd; + } + + if (rpcType == RpcType.Server) + return CreateServerRpcReaderMethod(typeDef, runLocally, originalMd, createdMd, serializedParameters, logicMd, rpcAttribute); + else if (rpcType == RpcType.Target || rpcType == RpcType.Observers) + return CreateClientRpcReaderMethod(serializedParameters, datas, cr); + else + return null; + } + + + /// + /// Creates a reader for ServerRpc. + /// + /// + /// + /// + private MethodDefinition CreateServerRpcReaderMethod(TypeDefinition typeDef, bool runLocally, MethodDefinition originalMd, MethodDefinition createdMd, List serializedParameters, MethodDefinition logicMd, CustomAttribute rpcAttribute) + { + ILProcessor processor = createdMd.Body.GetILProcessor(); + + bool requireOwnership = rpcAttribute.GetField(REQUIREOWNERSHIP_NAME, true); + //Create PooledReader parameter. + ParameterDefinition readerParameterDef = base.GetClass().CreateParameter(createdMd, base.GetClass().PooledReader_TypeRef); + + //Add connection parameter to the read method. Internals pass the connection into this. + ParameterDefinition channelParameterDef = GetOrCreateChannelParameter(createdMd, RpcType.Server); + ParameterDefinition connectionParameterDef = GetOrCreateNetworkConnectionParameter(createdMd); + + /* It's very important to read everything + * from the PooledReader before applying any + * exit logic. Should the method return before + * reading the data then anything after the rpc + * packet will be malformed due to invalid index. */ + VariableDefinition[] readVariableDefs; + List allReadInsts; + CreateRpcReadInstructions(createdMd, readerParameterDef, serializedParameters, out readVariableDefs, out allReadInsts); + + //Read to clear pooledreader. + processor.Add(allReadInsts); + + /* Don't continue if server is not active. + * This can happen if an object is deinitializing + * as a RPC arrives. When separate server and client + * this should not occur but there's a chance as host + * because deinitializations are slightly delayed to support + * the clientHost deinitializing the object as well. */ + base.GetClass().CreateIsServerCheck(createdMd, LoggingType.Off, false, false); + // + CreateServerRpcConditionsForServer(processor, requireOwnership, connectionParameterDef); + + //Block from running twice as host. + if (runLocally) + { + //The connection calling is always passed into the reader method as the last parameter. + ParameterDefinition ncPd = createdMd.Parameters[createdMd.Parameters.Count - 1]; + Instruction afterConnectionRet = processor.Create(OpCodes.Nop); + processor.Emit(OpCodes.Ldarg, ncPd); + MethodReference isLocalClientMr = base.GetClass().NetworkConnection_GetIsLocalClient_MethodRef; + processor.Emit(isLocalClientMr.GetCallOpCode(base.Session), isLocalClientMr); + processor.Emit(OpCodes.Brfalse_S, afterConnectionRet); + processor.Emit(OpCodes.Ret); + processor.Append(afterConnectionRet); + } + + //this.Logic + processor.Emit(OpCodes.Ldarg_0); + //Add each read variable as an argument. + foreach (VariableDefinition vd in readVariableDefs) + processor.Emit(OpCodes.Ldloc, vd); + + /* Pass in channel and connection if original + * method supports them. */ + ParameterDefinition originalChannelParameterDef = GetChannelParameter(originalMd, RpcType.Server); + ParameterDefinition originalConnectionParameterDef = GetNetworkConnectionParameter(originalMd); + if (originalChannelParameterDef != null) + processor.Emit(OpCodes.Ldarg, channelParameterDef); + if (originalConnectionParameterDef != null) + processor.Emit(OpCodes.Ldarg, connectionParameterDef); + //Call __Logic method. + processor.Emit(OpCodes.Call, logicMd); + processor.Emit(OpCodes.Ret); + + return createdMd; + } + + /// + /// Creates a reader for ObserversRpc. + /// + /// + /// + /// + private MethodDefinition CreateClientRpcReaderMethod(List serializedParameters, List attributeDatas, CreatedRpc cr) + { + MethodDefinition originalMd = cr.OriginalMethodDef; + MethodDefinition createdMd = cr.ReaderMethodDef; + RpcType rpcType = cr.RpcType; + CustomAttribute rpcAttribute = cr.Attribute; + bool runLocally = cr.RunLocally; + + ILProcessor processor = createdMd.Body.GetILProcessor(); + + //Create PooledReader parameter. + ParameterDefinition readerParameterDef = base.GetClass().CreateParameter(createdMd, base.GetClass().PooledReader_TypeRef); + ParameterDefinition channelParameterDef = GetOrCreateChannelParameter(createdMd, rpcType); + /* It's very important to read everything + * from the PooledReader before applying any + * exit logic. Should the method return before + * reading the data then anything after the rpc + * packet will be malformed due to invalid index. */ + VariableDefinition[] readVariableDefs; + List allReadInsts; + CreateRpcReadInstructions(createdMd, readerParameterDef, serializedParameters, out readVariableDefs, out allReadInsts); + //Read instructions even if not to include owner. + processor.Add(allReadInsts); + + /* Don't continue if client is not active. + * This can happen if an object is deinitializing + * as a RPC arrives. When separate server and client + * this should not occur but there's a chance as host + * because deinitializations are slightly delayed to support + * the clientHost deinitializing the object as well. */ + base.GetClass().CreateIsClientCheck(createdMd, LoggingType.Off, false, false); + + //Block from running twice as host. + if (runLocally) + processor.Add(CreateIsHostBlock(createdMd)); + + processor.Emit(OpCodes.Ldarg_0); //this. + /* TargetRpc passes in localconnection + * as receiver for connection. */ + if (rpcType == RpcType.Target) + { + processor.Emit(OpCodes.Ldarg_0); //this. + processor.Emit(OpCodes.Call, base.GetClass().LocalConnection_MethodRef); + } + else + { + //If this method uses target/observerRpc combined then load null for the connection. + RpcType allRpcTypes = attributeDatas.GetCombinedRpcType(); + if (allRpcTypes == (RpcType.Observers | RpcType.Target)) + processor.Emit(OpCodes.Ldnull); + } + //Add each read variable as an argument. + foreach (VariableDefinition vd in readVariableDefs) + processor.Emit(OpCodes.Ldloc, vd); + //Channel. + ParameterDefinition originalChannelParameterDef = GetChannelParameter(originalMd, rpcType); + if (originalChannelParameterDef != null) + processor.Emit(OpCodes.Ldarg, channelParameterDef); + //Call __Logic method. + processor.Emit(OpCodes.Call, cr.LogicMethodDef); + processor.Emit(OpCodes.Ret); + + return createdMd; + } + + + /// + /// Appends a block to the method if running as host. + /// + /// + private List CreateIsHostBlock(MethodDefinition md) + { + List ints = new List(); + ILProcessor processor = md.Body.GetILProcessor(); + + Instruction endIfInst = processor.Create(OpCodes.Nop); + ints.Add(processor.Create(OpCodes.Ldarg_0)); + ints.Add(processor.Create(OpCodes.Call, base.GetClass().IsHost_MethodRef)); + ints.Add(processor.Create(OpCodes.Brfalse_S, endIfInst)); + ints.Add(processor.Create(OpCodes.Ret)); + ints.Add(endIfInst); + + return ints; + } + + /// + /// Gets the optional NetworkConnection parameter for ServerRpc, if it exists. + /// + /// + /// + private ParameterDefinition GetNetworkConnectionParameter(MethodDefinition methodDef) + { + + ParameterDefinition result = methodDef.GetEndParameter(0); + //Is null, not networkconnection, or doesn't have default. + if (result == null || !result.Is(typeof(NetworkConnection)) || !result.HasDefault) + return null; + + return result; + } + + /// + /// Creates a NetworkConnection parameter if it's not the last or second to last parameter. + /// + /// + private ParameterDefinition GetOrCreateNetworkConnectionParameter(MethodDefinition methodDef) + { + ParameterDefinition result = GetNetworkConnectionParameter(methodDef); + if (result == null) + return base.GetClass().CreateParameter(methodDef, typeof(NetworkConnection), "conn"); + else + return result; + } + + /// + /// Returns the Channel parameter if it exist. + /// + /// + private ParameterDefinition GetChannelParameter(MethodDefinition methodDef, RpcType rpcType) + { + ParameterDefinition result = null; + ParameterDefinition pd = methodDef.GetEndParameter(0); + if (pd != null) + { + //Last parameter is channel. + if (pd.Is(typeof(Channel))) + { + result = pd; + } + /* Only other end parameter may be networkconnection. + * This can only be checked if a ServerRpc. */ + else if (rpcType == RpcType.Server) + { + //If last parameter is networkconnection and its default then can check second to last. + if (pd.Is(typeof(NetworkConnection)) && pd.HasDefault) + { + pd = methodDef.GetEndParameter(1); + if (pd != null && pd.Is(typeof(Channel))) + result = pd; + } + } + else + { + result = null; + } + } + + return result; + } + + /// + /// Creates a channel parameter if missing. + /// + /// + private ParameterDefinition GetOrCreateChannelParameter(MethodDefinition methodDef, RpcType rpcType) + { + ParameterDefinition result = GetChannelParameter(methodDef, rpcType); + //Add channel parameter if not included. + if (result == null) + { + ParameterDefinition connParameter = GetNetworkConnectionParameter(methodDef); + //If the connection parameter is specified then channel has to go before it. + if (connParameter != null) + return base.GetClass().CreateParameter(methodDef, typeof(Channel), "channel", ParameterAttributes.None, connParameter.Index); + //Not specified, add channel at end. + else + return base.GetClass().CreateParameter(methodDef, typeof(Channel), "channel"); + } + else + { + return result; + } + } + + /// + /// Creates a read for every writtenParameters and outputs variables read into, and instructions. + /// + /// + /// + /// + /// + /// + /// + private void CreateRpcReadInstructions(MethodDefinition methodDef, ParameterDefinition readerParameterDef, List serializedParameters, out VariableDefinition[] readVariableDefs, out List allReadInsts) + { + /* It's very important to read everything + * from the PooledReader before applying any + * exit logic. Should the method return before + * reading the data then anything after the rpc + * packet will be malformed due to invalid index. */ + readVariableDefs = new VariableDefinition[serializedParameters.Count]; + allReadInsts = new List(); + + //True if last parameter is a connection and a server rpc. + for (int i = 0; i < serializedParameters.Count; i++) + { + //Get read instructions and insert it before the return. + List insts = base.GetClass().CreateRead(methodDef, readerParameterDef, serializedParameters[i].ParameterType, out readVariableDefs[i]); + allReadInsts.AddRange(insts); + } + + } + /// + /// Creates conditions that clients must pass to send a ServerRpc. + /// + /// + /// + private void CreateServerRpcConditionsForClient(MethodDefinition methodDef, CustomAttribute rpcAttribute) + { + bool requireOwnership = rpcAttribute.GetField(REQUIREOWNERSHIP_NAME, true); + //If (!base.IsOwner); + if (requireOwnership) + base.GetClass().CreateLocalClientIsOwnerCheck(methodDef, LoggingType.Warning, false, false, true); + //If (!base.IsClient) + base.GetClass().CreateIsClientCheck(methodDef, LoggingType.Warning, false, true); + } + + /// + /// Creates conditions that server must pass to process a ServerRpc. + /// + /// + /// + /// Ret instruction. + private Instruction CreateServerRpcConditionsForServer(ILProcessor createdProcessor, bool requireOwnership, ParameterDefinition connectionParametereDef) + { + /* Don't need to check if server on receiving end. + * Next compare connection with owner. */ + //If (!base.CompareOwner); + if (requireOwnership) + return base.GetClass().CreateRemoteClientIsOwnerCheck(createdProcessor, connectionParametereDef); + else + return null; + } + + /// + /// Creates conditions that server must pass to process a ClientRpc. + /// + /// + private void CreateClientRpcConditionsForServer(MethodDefinition methodDef) + { + //If (!base.IsServer) + base.GetClass().CreateIsServerCheck(methodDef, LoggingType.Warning, false, false); + } + + /// + /// Creates a method containing the logic which will run when receiving the Rpc. + /// + /// + /// + private MethodDefinition CreateRpcLogicMethod(List datas, CreatedRpc cr, out bool intentionallyNull) + { + intentionallyNull = false; + + RpcType rpcType = cr.RpcType; + TypeDefinition typeDef = cr.TypeDef; + MethodDefinition originalMd = cr.OriginalMethodDef; + + + + //Methodname for logic methods do not use prefixes because there can be only one. + string methodName = $"{LOGIC_PREFIX}{GetMethodNameAsParameters(originalMd)}"; + /* If method already exist then just return it. This + * can occur when a method needs to be rebuilt due to + * inheritence, and renumbering the RPC method names. + * The logic method however does not need to be rewritten. */ + MethodDefinition logicMd = base.GetClass().CopyIntoNewMethod(originalMd, methodName, out _); + + cr.LogicMethodDef = logicMd; + return logicMd; + } + + /// + /// Finds and fixes call to base methods within remote calls + /// For example, changes `base.CmdDoSomething` to `base.UserCode_CmdDoSomething` within `this.UserCode_CmdDoSomething` + /// + /// + /// + private void RedirectBaseCall(MethodDefinition createdMethodDef, MethodDefinition originalMethodDef) + { + //All logic RPCs end with the logic suffix. + if (!createdMethodDef.Name.StartsWith(LOGIC_PREFIX)) + return; + //Not virtual, no need to check. + if (!createdMethodDef.IsVirtual) + return; + + foreach (Instruction instruction in createdMethodDef.Body.Instructions) + { + // if call to base.RpcDoSomething within this.RpcDoSOmething. + if (base.GetClass().IsCallToMethod(instruction, out MethodDefinition calledMethod) && calledMethod.Name == originalMethodDef.Name) + { + MethodReference baseLogicMd = createdMethodDef.DeclaringType.GetMethodDefinitionInAnyBase(base.Session, createdMethodDef.Name); + if (baseLogicMd == null) + { + base.LogError($"Could not find base method for {createdMethodDef.Name}."); + return; + } + + instruction.Operand = base.ImportReference(baseLogicMd); + } + } + } + + + /// + /// Redirects calls from the original Rpc method to the writer method. + /// + private void RedirectOriginalToWriter(List createdRpcs) + { + /* If there are multiple attributes/createdRpcs they will + * share the same originalMd so it's fine to take the first + * entry. */ + MethodDefinition originalMd = createdRpcs[0].OriginalMethodDef; + + + + ILProcessor processor = originalMd.Body.GetILProcessor(); + originalMd.Body.Instructions.Clear(); + + //If only one rpc type. + if (createdRpcs.Count == 1) + { + processor.Emit(OpCodes.Ldarg_0); //this. + //Parameters. + foreach (ParameterDefinition pd in originalMd.Parameters) + processor.Emit(OpCodes.Ldarg, pd); + + //Call method. + MethodReference writerMr = base.ImportReference(createdRpcs[0].WriterMethodDef); + processor.Emit(OpCodes.Call, writerMr); + + AddRunLocally(createdRpcs[0]); + } + //More than one which means it's an observer/targetRpc combo. + else + { + CreatedRpc observersRpc = createdRpcs.GetCreatedRpc(RpcType.Observers); + MethodReference observerWriterMr = base.ImportReference(observersRpc.WriterMethodDef); + + CreatedRpc targetRpc = createdRpcs.GetCreatedRpc(RpcType.Target); + MethodReference targetWriterMr = base.ImportReference(targetRpc.WriterMethodDef); + + Instruction targetRpcInst = processor.Create(OpCodes.Nop); + Instruction afterTargetRpcInst = processor.Create(OpCodes.Nop); + /* if (targetConn == null) + * WriteObserverRpc + * else + * WriteTargetRpc */ + processor.Emit(OpCodes.Ldarg, originalMd.Parameters[0]); + processor.Emit(OpCodes.Brtrue_S, targetRpcInst); + //Insert parameters. + processor.Emit(OpCodes.Ldarg_0); + foreach (ParameterDefinition pd in originalMd.Parameters) + processor.Emit(OpCodes.Ldarg, pd); + processor.Emit(OpCodes.Call, observerWriterMr); + AddRunLocally(observersRpc); + //else (target). + processor.Emit(OpCodes.Br_S, afterTargetRpcInst); + processor.Append(targetRpcInst); + //Insert parameters. + processor.Emit(OpCodes.Ldarg_0); + foreach (ParameterDefinition pd in originalMd.Parameters) + processor.Emit(OpCodes.Ldarg, pd); + processor.Emit(OpCodes.Call, targetWriterMr); + AddRunLocally(targetRpc); + processor.Append(afterTargetRpcInst); + } + + //Adds run locally logic if needed. + void AddRunLocally(CreatedRpc cRpc) + { + //Runlocally. + if (cRpc.RunLocally) + { + processor.Emit(OpCodes.Ldarg_0); //this. + //Parameters. + foreach (ParameterDefinition pd in originalMd.Parameters) + processor.Emit(OpCodes.Ldarg, pd); + processor.Emit(OpCodes.Call, cRpc.LogicMethodDef); + } + + } + + processor.Emit(OpCodes.Ret); + } + + + #region CreateSend + /// + /// Creates a call to SendServerRpc on NetworkBehaviour. + /// + /// + /// + private List CreateSendServerRpc(MethodDefinition methodDef, uint methodHash, VariableDefinition writerVariableDef, VariableDefinition channelVariableDef) + { + List insts = new List(); + ILProcessor processor = methodDef.Body.GetILProcessor(); + + insts.AddRange(CreateSendRpcCommon(processor, methodHash, writerVariableDef, channelVariableDef)); + //Call NetworkBehaviour. + insts.Add(processor.Create(OpCodes.Call, base.GetClass().SendServerRpc_MethodRef)); + + return insts; + } + + /// + /// Creates a call to SendObserversRpc on NetworkBehaviour. + /// + private List CreateSendObserversRpc(MethodDefinition methodDef, uint methodHash, VariableDefinition writerVariableDef, VariableDefinition channelVariableDef, CustomAttribute rpcAttribute) + { + List insts = new List(); + ILProcessor processor = methodDef.Body.GetILProcessor(); + + insts.AddRange(CreateSendRpcCommon(processor, methodHash, writerVariableDef, channelVariableDef)); + //Also add if buffered. + bool bufferLast = rpcAttribute.GetField(BUFFERLAST_NAME, false); + bool excludeOwner = rpcAttribute.GetField(EXCLUDEOWNER_NAME, false); + bool excludeServer = rpcAttribute.GetField(EXCLUDESERVER_NAME, false); + + //Warn user if any values are byref. + bool usedByref = false; + foreach (ParameterDefinition item in methodDef.Parameters) + { + if (item.IsIn) + { + usedByref = true; + break; + } + } + if (usedByref) + base.LogWarning($"Method {methodDef.FullName} takes an argument by reference. While this is supported, using BufferLast in addition to by reference arguements will buffer the value as it was serialized, not as it is when sending buffered."); + + insts.Add(processor.Create(OpCodes.Ldc_I4, bufferLast.ToInt())); + insts.Add(processor.Create(OpCodes.Ldc_I4, excludeServer.ToInt())); + insts.Add(processor.Create(OpCodes.Ldc_I4, excludeOwner.ToInt())); + //Call NetworkBehaviour. + insts.Add(processor.Create(OpCodes.Call, base.GetClass().SendObserversRpc_MethodRef)); + + return insts; + } + /// + /// Creates a call to SendTargetRpc on NetworkBehaviour. + /// + private List CreateSendTargetRpc(MethodDefinition methodDef, uint methodHash, VariableDefinition writerVariableDef, VariableDefinition channelVariableDef, ParameterDefinition targetConnectionParameterDef, List attributeDatas) + { + List insts = new List(); + ILProcessor processor = methodDef.Body.GetILProcessor(); + + CustomAttribute rpcAttribute = attributeDatas.GetAttribute(base.Session, RpcType.Target); + bool validateTarget = rpcAttribute.GetField(VALIDATETARGET_NAME, true); + bool excludeServer = rpcAttribute.GetField(EXCLUDESERVER_NAME, false); + + insts.AddRange(CreateSendRpcCommon(processor, methodHash, writerVariableDef, channelVariableDef)); + //Reference to NetworkConnection that RPC is going to. + insts.Add(processor.Create(OpCodes.Ldarg, targetConnectionParameterDef)); + //Exclude server from rpc. + insts.Add(processor.Create(OpCodes.Ldc_I4, excludeServer.ToInt())); + //Validate target receiving the rpc. + insts.Add(processor.Create(OpCodes.Ldc_I4, validateTarget.ToInt())); + //Call NetworkBehaviour. + insts.Add(processor.Create(OpCodes.Call, base.GetClass().SendTargetRpc_MethodRef)); + + return insts; + } + + /// + /// Writes common properties that all SendRpc methods use. + /// + private List CreateSendRpcCommon(ILProcessor processor, uint methodHash, VariableDefinition writerVariableDef, VariableDefinition channelVariableDef) + { + List insts = new List(); + + insts.Add(processor.Create(OpCodes.Ldarg_0)); // argument: this + insts.Add(processor.Create(OpCodes.Ldc_I4, (int)methodHash)); + //reference to PooledWriter. + insts.Add(processor.Create(OpCodes.Ldloc, writerVariableDef)); + //reference to Channel. + insts.Add(processor.Create(OpCodes.Ldloc, channelVariableDef)); + + return insts; + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Processing/Rpc/RpcProcessor.cs.meta b/Assets/FishNet/CodeGenerating/Processing/Rpc/RpcProcessor.cs.meta new file mode 100644 index 0000000..e87895d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Rpc/RpcProcessor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4d4adb5891ee44f4397cd07ac2df0ce0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Processing/Typed.meta b/Assets/FishNet/CodeGenerating/Processing/Typed.meta new file mode 100644 index 0000000..8481191 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Typed.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1c24efb514a41fb41b4eb883a5f51fb5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Processing/Typed/ProcessedSync.cs b/Assets/FishNet/CodeGenerating/Processing/Typed/ProcessedSync.cs new file mode 100644 index 0000000..5cc1130 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Typed/ProcessedSync.cs @@ -0,0 +1,24 @@ +using MonoFN.Cecil; + +namespace FishNet.CodeGenerating.Processing +{ + + public class ProcessedSync + { + public FieldReference OriginalFieldRef; + public FieldReference GeneratedFieldRef; + public MethodReference SetMethodRef; + public MethodReference GetMethodRef; + + public ProcessedSync(FieldReference originalFieldRef,FieldReference generatedFieldRef, MethodReference setMethodRef, MethodReference getMethodRef) + { + OriginalFieldRef = originalFieldRef; + GeneratedFieldRef = generatedFieldRef; + SetMethodRef = setMethodRef; + GetMethodRef = getMethodRef; + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Processing/Typed/ProcessedSync.cs.meta b/Assets/FishNet/CodeGenerating/Processing/Typed/ProcessedSync.cs.meta new file mode 100644 index 0000000..1d82e02 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Typed/ProcessedSync.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c0dc2fea60bfe1341b04e7165251d36f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Processing/WriterProcessor.cs b/Assets/FishNet/CodeGenerating/Processing/WriterProcessor.cs new file mode 100644 index 0000000..38d662b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/WriterProcessor.cs @@ -0,0 +1,1158 @@ +using FishNet.CodeGenerating.Extension; +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.CodeGenerating.ILCore; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Utility.Performance; +using MonoFN.Cecil; +using MonoFN.Cecil.Cil; +using MonoFN.Cecil.Rocks; +using System; +using System.Collections.Generic; +using SR = System.Reflection; +using UnityDebug = UnityEngine.Debug; + +namespace FishNet.CodeGenerating.Helping +{ + + internal class WriterProcessor : CodegenBase + { + #region Reflection references. + public readonly Dictionary InstancedWriterMethods = new Dictionary(); + public readonly Dictionary StaticWriterMethods = new Dictionary(); + public HashSet AutoPackedMethods = new HashSet(new TypeReferenceComparer()); + + public TypeDefinition GeneratedWriterClassTypeDef; + public MethodDefinition GeneratedWriterOnLoadMethodDef; + #endregion + + #region Misc. + /// + /// TypeReferences which have already had delegates made for. + /// + private HashSet _delegatedTypes = new HashSet(); + #endregion + + #region Const. + /// + /// Namespace to use for generated serializers and delegates. + /// + public const string GENERATED_WRITER_NAMESPACE = "FishNet.Serializing.Generated"; + /// + /// Name to use for generated serializers class. + /// + public const string GENERATED_WRITERS_CLASS_NAME = "GeneratedWriters___Internal"; + /// + /// Attributes to use for generated serializers class. + /// + public const TypeAttributes GENERATED_TYPE_ATTRIBUTES = (TypeAttributes.BeforeFieldInit | TypeAttributes.Class | TypeAttributes.AnsiClass | + TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.Abstract | TypeAttributes.Sealed); + /// + /// Name to use for InitializeOnce method. + /// + public const string INITIALIZEONCE_METHOD_NAME = "InitializeOnce"; + /// + /// Attributes to use for InitializeOnce method within generated serializer classes. + /// + public const MethodAttributes INITIALIZEONCE_METHOD_ATTRIBUTES = (MethodAttributes.Static | MethodAttributes.Private | MethodAttributes.HideBySig); + /// + /// Attritbutes to use for generated serializers. + /// + public const MethodAttributes GENERATED_METHOD_ATTRIBUTES = (MethodAttributes.Static | MethodAttributes.Public | MethodAttributes.HideBySig); + /// + /// Prefix all built-in and user created write methods should begin with. + /// + internal const string WRITE_PREFIX = "Write"; + /// + /// Prefix all built-in and user created write methods should begin with. + /// + internal const string GENERATED_WRITE_PREFIX = "Write___"; + /// + /// Types to exclude from being scanned for auto serialization. + /// + public static readonly System.Type[] EXCLUDED_AUTO_SERIALIZER_TYPES = new System.Type[] + { + typeof(NetworkBehaviour) + }; + /// + /// Types within assemblies which begin with these prefixes will not have serializers created for them. + /// + public static readonly string[] EXCLUDED_ASSEMBLY_PREFIXES = new string[] + { + "UnityEngine." + }; + #endregion + + public override bool ImportReferences() => true; + + /// + /// Processes data. To be used after everything else has called ImportReferences. + /// + /// + public bool Process() + { + GeneralHelper gh = base.GetClass(); + + CreateGeneratedClassData(); + FindInstancedWriters(); + CreateInstancedWriterExtensions(); + + //Creates class for generated writers, and init on load method. + void CreateGeneratedClassData() + { + GeneratedWriterClassTypeDef = gh.GetOrCreateClass(out _, GENERATED_TYPE_ATTRIBUTES, GENERATED_WRITERS_CLASS_NAME, null, WriterProcessor.GENERATED_WRITER_NAMESPACE); + /* If constructor isn't set then try to get or create it + * and also add it to methods if were created. */ + GeneratedWriterOnLoadMethodDef = gh.GetOrCreateMethod(GeneratedWriterClassTypeDef, out _, INITIALIZEONCE_METHOD_ATTRIBUTES, INITIALIZEONCE_METHOD_NAME, base.Module.TypeSystem.Void); + ILProcessor pp = GeneratedWriterOnLoadMethodDef.Body.GetILProcessor(); + pp.Emit(OpCodes.Ret); + gh.CreateRuntimeInitializeOnLoadMethodAttribute(GeneratedWriterOnLoadMethodDef); + } + + //Finds all instanced writers and autopack types. + void FindInstancedWriters() + { + Type pooledWriterType = typeof(PooledWriter); + foreach (SR.MethodInfo methodInfo in pooledWriterType.GetMethods()) + { + if (IsSpecialWriteMethod(methodInfo)) + continue; + bool autoPackMethod; + if (IsIgnoredWriteMethod(methodInfo, out autoPackMethod)) + continue; + + MethodReference methodRef = base.ImportReference(methodInfo); + /* TypeReference for the first parameter in the write method. + * The first parameter will always be the type written. */ + TypeReference typeRef = base.ImportReference(methodRef.Parameters[0].ParameterType); + /* If here all checks pass. */ + AddWriterMethod(typeRef, methodRef, true, true); + if (autoPackMethod) + AutoPackedMethods.Add(typeRef); + } + } + + return true; + } + + /// + /// Returns if a MethodInfo is considered a special write method. + /// Special write methods have declared references within this class, and will not have extensions made for them. + /// + public bool IsSpecialWriteMethod(SR.MethodInfo methodInfo) + { + /* Special methods. */ + if (methodInfo.Name == nameof(PooledWriter.Dispose)) + return true; + else if (methodInfo.Name == nameof(PooledWriter.WritePackedWhole)) + return true; + else if (methodInfo.Name == nameof(PooledWriter.WriteDictionary)) + return true; + else if (methodInfo.Name == nameof(PooledWriter.WriteList)) + return true; + + return false; + } + + /// + /// Returns if a write method should be ignored. + /// + public bool IsIgnoredWriteMethod(SR.MethodInfo methodInfo, out bool autoPackMethod) + { + autoPackMethod = false; + + if (base.GetClass().CodegenExclude(methodInfo)) + return true; + //Not long enough to be a write method. + else if (methodInfo.Name.Length < WRITE_PREFIX.Length) + return true; + //Method name doesn't start with writePrefix. + else if (methodInfo.Name.Substring(0, WRITE_PREFIX.Length) != WRITE_PREFIX) + return true; + + SR.ParameterInfo[] parameterInfos = methodInfo.GetParameters(); + /* No parameters or more than 2 parameters. Most Write methods + * will have only 1 parameter but some will have 2 if + * there is a pack option. */ + if (parameterInfos.Length < 1 || parameterInfos.Length > 2) + return true; + /* If two parameters make sure the second parameter + * is a pack parameter. */ + if (parameterInfos.Length == 2) + { + autoPackMethod = (parameterInfos[1].ParameterType == typeof(AutoPackType)); + if (!autoPackMethod) + return true; + } + + return false; + } + + + /// + /// Creates writer extension methods for built-in writers. + /// + private void CreateInstancedWriterExtensions() + { + //return; + if (!FishNetILPP.IsFishNetAssembly(base.Session)) + return; + + GeneralHelper gh = base.GetClass(); + WriterProcessor gwh = base.GetClass(); + + //List staticReaders = new List(); + foreach (KeyValuePair item in InstancedWriterMethods) + { + MethodReference instancedWriteMr = item.Value; + if (instancedWriteMr.HasGenericParameters) + continue; + + TypeReference valueTr = instancedWriteMr.Parameters[0].ParameterType; + + MethodDefinition md = new MethodDefinition($"InstancedExtension___{instancedWriteMr.Name}", + WriterProcessor.GENERATED_METHOD_ATTRIBUTES, + base.Module.TypeSystem.Void); + + //Add extension parameter. + ParameterDefinition writerPd = gh.CreateParameter(md, typeof(Writer), "writer"); + //Add parameters needed by instanced writer. + List otherPds = md.CreateParameters(base.Session, instancedWriteMr); + gh.MakeExtensionMethod(md); + // + gwh.GeneratedWriterClassTypeDef.Methods.Add(md); + + ILProcessor processor = md.Body.GetILProcessor(); + //Load writer. + processor.Emit(OpCodes.Ldarg, writerPd); + //Load args. + foreach (ParameterDefinition pd in otherPds) + processor.Emit(OpCodes.Ldarg, pd); + //Call instanced. + processor.Emit(instancedWriteMr.GetCallOpCode(base.Session), instancedWriteMr); + processor.Emit(OpCodes.Ret); + AddWriterMethod(valueTr, md, false, true); + } + + } + + /// + /// Adds typeRef, methodDef to Instanced or Static write methods. + /// + public void AddWriterMethod(TypeReference typeRef, MethodReference methodRef, bool instanced, bool useAdd) + { + Dictionary dict = (instanced) ? + InstancedWriterMethods : StaticWriterMethods; + string fullName = typeRef.GetFullnameWithoutBrackets(); + if (useAdd) + dict.Add(fullName, methodRef); + else + dict[fullName] = methodRef; + } + + /// + /// Removes typeRef from Instanced or Static write methods. + /// + internal void RemoveWriterMethod(TypeReference typeRef, bool instanced) + { + Dictionary dict = (instanced) ? + InstancedWriterMethods : StaticWriterMethods; + + dict.Remove(typeRef.FullName); + } + + /// + /// Returns if typeRef supports auto packing. + /// + public bool IsAutoPackedType(TypeReference typeRef) + { + return AutoPackedMethods.Contains(typeRef); + } + + + /// + /// Creates Write delegates for known static methods. + /// + public void CreateStaticMethodDelegates() + { + foreach (KeyValuePair item in StaticWriterMethods) + base.GetClass().CreateStaticMethodWriteDelegate(item.Value); + } + + /// + /// Creates a Write delegate for writeMethodRef and places it within the generated reader/writer constructor. + /// + /// + private void CreateStaticMethodWriteDelegate(MethodReference writeMr) + { + GeneralHelper gh = base.GetClass(); + WriterImports wi = base.GetClass(); + + //Check if ret already exist, if so remove it; ret will be added on again in this method. + if (GeneratedWriterOnLoadMethodDef.Body.Instructions.Count != 0) + { + int lastIndex = (GeneratedWriterOnLoadMethodDef.Body.Instructions.Count - 1); + if (GeneratedWriterOnLoadMethodDef.Body.Instructions[lastIndex].OpCode == OpCodes.Ret) + GeneratedWriterOnLoadMethodDef.Body.Instructions.RemoveAt(lastIndex); + } + + ILProcessor processor = GeneratedWriterOnLoadMethodDef.Body.GetILProcessor(); + TypeReference dataTypeRef; + dataTypeRef = writeMr.Parameters[1].ParameterType; + + //Check if writer already exist. + if (_delegatedTypes.Contains(dataTypeRef)) + { + base.LogError($"Generic write already created for {dataTypeRef.FullName}."); + return; + } + else + { + _delegatedTypes.Add(dataTypeRef); + } + + /* Create a Action delegate. + * May also be Action delegate + * for packed types. */ + processor.Emit(OpCodes.Ldnull); + processor.Emit(OpCodes.Ldftn, writeMr); + + GenericInstanceType actionGenericInstance; + MethodReference actionConstructorInstanceMethodRef; + bool isAutoPacked = base.GetClass().IsAutoPackedType(dataTypeRef); + + //Generate for auto pack type. + if (isAutoPacked) + { + actionGenericInstance = gh.ActionT3_TypeRef.MakeGenericInstanceType(wi.WriterTypeRef, dataTypeRef, base.GetClass().AutoPackTypeRef); + actionConstructorInstanceMethodRef = gh.ActionT3Constructor_MethodRef.MakeHostInstanceGeneric(base.Session, actionGenericInstance); + } + //Generate for normal type. + else + { + actionGenericInstance = gh.ActionT2_TypeRef.MakeGenericInstanceType(wi.WriterTypeRef, dataTypeRef); + actionConstructorInstanceMethodRef = gh.ActionT2Constructor_MethodRef.MakeHostInstanceGeneric(base.Session, actionGenericInstance); + } + + processor.Emit(OpCodes.Newobj, actionConstructorInstanceMethodRef); + //Call delegate to GenericWriter.Write + GenericInstanceType genericInstance = wi.GenericWriterTypeRef.MakeGenericInstanceType(dataTypeRef); + MethodReference genericrWriteMethodRef = (isAutoPacked) ? + wi.WriteAutoPackGetSetMethodRef.MakeHostInstanceGeneric(base.Session, genericInstance) : + wi.WriteGetSetMethodRef.MakeHostInstanceGeneric(base.Session, genericInstance); + processor.Emit(OpCodes.Call, genericrWriteMethodRef); + + processor.Emit(OpCodes.Ret); + } + + + + /// + /// Returns if typeRef has a serializer. + /// + /// + /// + internal bool HasSerializer(TypeReference typeRef, bool createMissing) + { + bool result = (GetInstancedWriteMethodReference(typeRef) != null) || + (GetStaticWriteMethodReference(typeRef) != null); + + if (!result && createMissing) + { + if (!base.GetClass().HasNonSerializableAttribute(typeRef.CachedResolve(base.Session))) + { + MethodReference methodRef = CreateWriter(typeRef); + result = (methodRef != null); + } + } + + return result; + } + + + #region GetWriterMethodReference. + /// + /// Returns the MethodReference for typeRef. + /// + /// + /// + internal MethodReference GetInstancedWriteMethodReference(TypeReference typeRef) + { + string fullName = typeRef.GetFullnameWithoutBrackets(); + InstancedWriterMethods.TryGetValue(fullName, out MethodReference methodRef); + return methodRef; + } + /// + /// Returns the MethodReference for typeRef. + /// + /// + /// + internal MethodReference GetStaticWriteMethodReference(TypeReference typeRef) + { + string fullName = typeRef.GetFullnameWithoutBrackets(); + StaticWriterMethods.TryGetValue(fullName, out MethodReference methodRef); + return methodRef; + } + + /// + /// Returns the MethodReference for typeRef favoring instanced or static. + /// + /// + /// + /// + internal MethodReference GetWriteMethodReference(TypeReference typeRef) + { + bool favorInstanced = false; + + MethodReference result; + if (favorInstanced) + { + result = GetInstancedWriteMethodReference(typeRef); + if (result == null) + result = GetStaticWriteMethodReference(typeRef); + } + else + { + result = GetStaticWriteMethodReference(typeRef); + if (result == null) + result = GetInstancedWriteMethodReference(typeRef); + } + + return result; + } + /// + /// Gets the write MethodRef for typeRef, or tries to create it if not present. + /// + /// + /// + internal MethodReference GetOrCreateWriteMethodReference(TypeReference typeRef) + { +#pragma warning disable CS0219 + bool favorInstanced = false; +#pragma warning restore CS0219 + //Try to get existing writer, if not present make one. + MethodReference writeMethodRef = GetWriteMethodReference(typeRef); + if (writeMethodRef == null) + writeMethodRef = CreateWriter(typeRef); + + //If still null then return could not be generated. + if (writeMethodRef == null) + { + base.LogError($"Could not create serializer for {typeRef.FullName}."); + } + //Otherwise, check if generic and create writes for generic pararameters. + else if (typeRef.IsGenericInstance) + { + GenericInstanceType git = (GenericInstanceType)typeRef; + foreach (TypeReference item in git.GenericArguments) + { + MethodReference result = GetOrCreateWriteMethodReference(item); + if (result == null) + { + base.LogError($"Could not create serializer for {item.FullName}."); + return null; + } + } + } + + return writeMethodRef; + } + #endregion + + + /// + /// Creates a PooledWriter within the body/ and returns its variable index. + /// EG: PooledWriter writer = WriterPool.GetWriter(); + /// + internal VariableDefinition CreatePooledWriter(MethodDefinition methodDef, int length) + { + VariableDefinition resultVd; + List insts = CreatePooledWriter(methodDef, length, out resultVd); + + ILProcessor processor = methodDef.Body.GetILProcessor(); + processor.Add(insts); + return resultVd; + } + /// + /// Creates a PooledWriter within the body/ and returns its variable index. + /// EG: PooledWriter writer = WriterPool.GetWriter(); + /// + /// + /// + /// + internal List CreatePooledWriter(MethodDefinition methodDef, int length, out VariableDefinition resultVd) + { + WriterImports wi = base.GetClass(); + + List insts = new List(); + ILProcessor processor = methodDef.Body.GetILProcessor(); + + resultVd = base.GetClass().CreateVariable(methodDef, wi.PooledWriter_TypeRef); + //If length is specified then pass in length. + if (length > 0) + { + insts.Add(processor.Create(OpCodes.Ldc_I4, length)); + insts.Add(processor.Create(OpCodes.Call, wi.WriterPool_GetWriterLength_MethodRef)); + } + //Use parameter-less method if no length. + else + { + insts.Add(processor.Create(OpCodes.Call, wi.WriterPool_GetWriter_MethodRef)); + } + //Set value to variable definition. + insts.Add(processor.Create(OpCodes.Stloc, resultVd)); + return insts; + } + + + /// + /// Calls Dispose on a PooledWriter. + /// EG: writer.Dispose(); + /// + /// + /// + internal List DisposePooledWriter(MethodDefinition methodDef, VariableDefinition writerDefinition) + { + WriterImports wi = base.GetClass(); + + List insts = new List(); + ILProcessor processor = methodDef.Body.GetILProcessor(); + + insts.Add(processor.Create(OpCodes.Ldloc, writerDefinition)); + insts.Add(processor.Create(wi.PooledWriter_Dispose_MethodRef.GetCallOpCode(base.Session), wi.PooledWriter_Dispose_MethodRef)); + + return insts; + } + + + /// + /// Creates a null check on the second argument using a boolean. + /// + internal void CreateRetOnNull(ILProcessor processor, ParameterDefinition writerParameterDef, ParameterDefinition checkedParameterDef, bool useBool) + { + Instruction endIf = processor.Create(OpCodes.Nop); + //If (value) jmp to endIf. + processor.Emit(OpCodes.Ldarg, checkedParameterDef); + processor.Emit(OpCodes.Brtrue, endIf); + //writer.WriteBool / writer.WritePackedWhole + if (useBool) + CreateWriteBool(processor, writerParameterDef, true); + else + CreateWritePackedWhole(processor, writerParameterDef, -1); + //Exit method. + processor.Emit(OpCodes.Ret); + //End of if check. + processor.Append(endIf); + } + + #region CreateWritePackWhole + /// + /// Creates a call to WritePackWhole with value. + /// + /// + /// + internal void CreateWritePackedWhole(ILProcessor processor, ParameterDefinition writerParameterDef, int value) + { + WriterImports wi = base.GetClass(); + + //Create local int and set it to value. + VariableDefinition intVariableDef = base.GetClass().CreateVariable(processor.Body.Method, typeof(int)); + base.GetClass().SetVariableDefinitionFromInt(processor, intVariableDef, value); + //Writer. + processor.Emit(OpCodes.Ldarg, writerParameterDef); + //Writer.WritePackedWhole(value). + processor.Emit(OpCodes.Ldloc, intVariableDef); + processor.Emit(OpCodes.Conv_U8); + processor.Emit(wi.Writer_WritePackedWhole_MethodRef.GetCallOpCode(base.Session), wi.Writer_WritePackedWhole_MethodRef); + } + /// + /// Creates a call to WritePackWhole with value. + /// + /// + /// + internal void CreateWritePackedWhole(ILProcessor processor, ParameterDefinition writerParameterDef, VariableDefinition value) + { + WriterImports wi = base.GetClass(); + + //Writer. + processor.Emit(OpCodes.Ldarg, writerParameterDef); + //Writer.WritePackedWhole(value). + processor.Emit(OpCodes.Ldloc, value); + processor.Emit(OpCodes.Conv_U8); + processor.Emit(wi.Writer_WritePackedWhole_MethodRef.GetCallOpCode(base.Session), wi.Writer_WritePackedWhole_MethodRef); + } + #endregion + + /// + /// Creates a call to WriteBoolean with value. + /// + /// + /// + /// + internal void CreateWriteBool(ILProcessor processor, ParameterDefinition writerParameterDef, bool value) + { + MethodReference writeBoolMethodRef = GetWriteMethodReference(base.GetClass().GetTypeReference(typeof(bool))); + processor.Emit(OpCodes.Ldarg, writerParameterDef); + int intValue = (value) ? 1 : 0; + processor.Emit(OpCodes.Ldc_I4, intValue); + processor.Emit(writeBoolMethodRef.GetCallOpCode(base.Session), writeBoolMethodRef); + } + + /// + /// Creates a Write call on a PooledWriter variable for parameterDef. + /// EG: writer.WriteBool(xxxxx); + /// + internal List CreateWriteInstructions(MethodDefinition methodDef, object pooledWriterDef, ParameterDefinition valueParameterDef, MethodReference writeMr) + { + List insts = new List(); + ILProcessor processor = methodDef.Body.GetILProcessor(); + + if (writeMr != null) + { + if (pooledWriterDef is VariableDefinition) + { + insts.Add(processor.Create(OpCodes.Ldloc, (VariableDefinition)pooledWriterDef)); + } + else if (pooledWriterDef is ParameterDefinition) + { + insts.Add(processor.Create(OpCodes.Ldarg, (ParameterDefinition)pooledWriterDef)); + } + else + { + base.LogError($"{pooledWriterDef.GetType().FullName} is not a valid writerDef. Type must be VariableDefinition or ParameterDefinition."); + return new List(); + } + insts.Add(processor.Create(OpCodes.Ldarg, valueParameterDef)); + //If an auto pack method then insert default value. + if (AutoPackedMethods.Contains(valueParameterDef.ParameterType)) + { + AutoPackType packType = base.GetClass().GetDefaultAutoPackType(valueParameterDef.ParameterType); + insts.Add(processor.Create(OpCodes.Ldc_I4, (int)packType)); + } + + TypeReference valueTr = valueParameterDef.ParameterType; + /* If generic then find write class for + * data type. Currently we only support one generic + * for this. */ + if (valueTr.IsGenericInstance) + { + GenericInstanceType git = (GenericInstanceType)valueTr; + TypeReference genericTr = git.GenericArguments[0]; + writeMr = writeMr.GetMethodReference(base.Session, genericTr); + } + + insts.Add(processor.Create(OpCodes.Call, writeMr)); + return insts; + } + else + { + base.LogError($"Writer not found for {valueParameterDef.ParameterType.FullName}."); + return new List(); + } + } + /// + /// Creates a Write call on a PooledWriter variable for parameterDef. + /// EG: writer.WriteBool(xxxxx); + /// + internal void CreateWrite(MethodDefinition methodDef, object writerDef, ParameterDefinition valuePd, MethodReference writeMr) + { + List insts = CreateWriteInstructions(methodDef, writerDef, valuePd, writeMr); + ILProcessor processor = methodDef.Body.GetILProcessor(); + processor.Add(insts); + } + /// + /// Creates a Write call to a writer. + /// EG: StaticClass.WriteBool(xxxxx); + /// + /// + /// + internal void CreateWrite(MethodDefinition writerMd, ParameterDefinition valuePd, FieldDefinition fieldDef, MethodReference writeMr) + { + if (writeMr != null) + { + ILProcessor processor = writerMd.Body.GetILProcessor(); + ParameterDefinition writerPd = writerMd.Parameters[0]; + + /* If generic then find write class for + * data type. Currently we only support one generic + * for this. */ + if (fieldDef.FieldType.IsGenericInstance) + { + GenericInstanceType git = (GenericInstanceType)fieldDef.FieldType; + TypeReference genericTr = git.GenericArguments[0]; + writeMr = writeMr.GetMethodReference(base.Session, genericTr); + } + + FieldReference fieldRef = base.GetClass().GetFieldReference(fieldDef); + processor.Emit(OpCodes.Ldarg, writerPd); + processor.Emit(OpCodes.Ldarg, valuePd); + processor.Emit(OpCodes.Ldfld, fieldRef); + //If an auto pack method then insert default value. + if (AutoPackedMethods.Contains(fieldDef.FieldType)) + { + AutoPackType packType = base.GetClass().GetDefaultAutoPackType(fieldDef.FieldType); + processor.Emit(OpCodes.Ldc_I4, (int)packType); + } + processor.Emit(OpCodes.Call, writeMr); + } + else + { + base.LogError($"Writer not found for {fieldDef.FieldType.FullName}."); + } + } + + /// + /// Creates a Write call to a writer. + /// EG: StaticClass.WriteBool(xxxxx); + /// + /// + /// + internal void CreateWrite(MethodDefinition writerMd, ParameterDefinition valuePd, MethodReference getMr, MethodReference writeMr) + { + TypeReference returnTr = base.ImportReference(getMr.ReturnType); + + if (writeMr != null) + { + ILProcessor processor = writerMd.Body.GetILProcessor(); + ParameterDefinition writerPd = writerMd.Parameters[0]; + + /* If generic then find write class for + * data type. Currently we only support one generic + * for this. */ + if (returnTr.IsGenericInstance) + { + GenericInstanceType git = (GenericInstanceType)returnTr; + TypeReference genericTr = git.GenericArguments[0]; + writeMr = writeMr.GetMethodReference(base.Session, genericTr); + } + + processor.Emit(OpCodes.Ldarg, writerPd); + OpCode ldArgOC0 = (valuePd.ParameterType.IsValueType) ? OpCodes.Ldarga : OpCodes.Ldarg; + processor.Emit(ldArgOC0, valuePd); + processor.Emit(OpCodes.Call, getMr); + //If an auto pack method then insert default value. + if (AutoPackedMethods.Contains(returnTr)) + { + AutoPackType packType = base.GetClass().GetDefaultAutoPackType(returnTr); + processor.Emit(OpCodes.Ldc_I4, (int)packType); + } + processor.Emit(OpCodes.Call, writeMr); + } + else + { + base.LogError($"Writer not found for {returnTr.FullName}."); + } + } + + + + #region TypeReference writer generators. + + /// + /// Generates a writer for objectTypeReference if one does not already exist. + /// + /// + /// + internal MethodReference CreateWriter(TypeReference objectTr) + { + MethodReference methodRefResult = null; + TypeDefinition objectTd; + SerializerType serializerType = base.GetClass().GetSerializerType(objectTr, true, out objectTd); + if (serializerType != SerializerType.Invalid) + { + //Array. + if (serializerType == SerializerType.Array) + methodRefResult = CreateArrayWriterMethodReference(objectTr); + //Enum. + else if (serializerType == SerializerType.Enum) + methodRefResult = CreateEnumWriterMethodDefinition(objectTr); + //Dictionary, List, ListCache + else if (serializerType == SerializerType.Dictionary + || serializerType == SerializerType.List + || serializerType == SerializerType.ListCache) + methodRefResult = CreateGenericCollectionWriterMethodReference(objectTr, serializerType); + //NetworkBehaviour. + else if (serializerType == SerializerType.NetworkBehaviour) + methodRefResult = CreateNetworkBehaviourWriterMethodReference(objectTd); + //Nullable type. + else if (serializerType == SerializerType.Nullable) + methodRefResult = CreateNullableWriterMethodReference(objectTr, objectTd); + //Class or struct. + else if (serializerType == SerializerType.ClassOrStruct) + methodRefResult = CreateClassOrStructWriterMethodDefinition(objectTr); + } + + //If was not created. + if (methodRefResult == null) + RemoveFromStaticWriters(objectTr); + + return methodRefResult; + } + + /// + /// Removes from static writers. + /// + private void RemoveFromStaticWriters(TypeReference tr) + { + base.GetClass().RemoveWriterMethod(tr, false); + } + /// + /// Adds to static writers. + /// + private void AddToStaticWriters(TypeReference tr, MethodReference mr) + { + base.GetClass().AddWriterMethod(tr, mr.CachedResolve(base.Session), false, true); + } + + /// + /// Adds a write for a NetworkBehaviour class type to WriterMethods. + /// + /// + private MethodReference CreateNetworkBehaviourWriterMethodReference(TypeReference objectTr) + { + ObjectHelper oh = base.GetClass(); + + objectTr = base.ImportReference(objectTr.Resolve()); + //All NetworkBehaviour types will simply WriteNetworkBehaviour/ReadNetworkBehaviour. + //Create generated reader/writer class. This class holds all generated reader/writers. + base.GetClass().GetOrCreateClass(out _, GENERATED_TYPE_ATTRIBUTES, GENERATED_WRITERS_CLASS_NAME, null); + + MethodDefinition createdWriterMd = CreateStaticWriterStubMethodDefinition(objectTr); + AddToStaticWriters(objectTr, createdWriterMd); + + ILProcessor processor = createdWriterMd.Body.GetILProcessor(); + + MethodReference writeMethodRef = base.GetClass().GetOrCreateWriteMethodReference(oh.NetworkBehaviour_TypeRef); + //Get parameters for method. + ParameterDefinition writerParameterDef = createdWriterMd.Parameters[0]; + ParameterDefinition classParameterDef = createdWriterMd.Parameters[1]; + + //Load parameters as arguments. + processor.Emit(OpCodes.Ldarg, writerParameterDef); + processor.Emit(OpCodes.Ldarg, classParameterDef); + //writer.WriteNetworkBehaviour(arg1); + processor.Emit(OpCodes.Call, writeMethodRef); + + processor.Emit(OpCodes.Ret); + + return base.ImportReference(createdWriterMd); + } + + /// + /// Gets the length of a collection and writes the value to a variable. + /// + private void CreateCollectionLength(ILProcessor processor, ParameterDefinition collectionParameterDef, VariableDefinition storeVariableDef) + { + processor.Emit(OpCodes.Ldarg, collectionParameterDef); + processor.Emit(OpCodes.Ldlen); + processor.Emit(OpCodes.Conv_I4); + processor.Emit(OpCodes.Stloc, storeVariableDef); + } + + + /// + /// Creates a writer for a class or struct of objectTypeRef. + /// + /// + /// + private MethodReference CreateNullableWriterMethodReference(TypeReference objectTr, TypeDefinition objectTd) + { + WriterProcessor wh = base.GetClass(); + + GenericInstanceType objectGit = objectTr as GenericInstanceType; + TypeReference valueTr = objectGit.GenericArguments[0]; + + //Get the writer for the value. + MethodReference valueWriterMr = wh.GetOrCreateWriteMethodReference(valueTr); + if (valueWriterMr == null) + return null; + + + MethodDefinition tmpMd; + tmpMd = objectTd.GetMethod("get_Value"); + MethodReference genericGetValueMr = tmpMd.MakeHostInstanceGeneric(base.Session, objectGit); + tmpMd = objectTd.GetMethod("get_HasValue"); + MethodReference genericHasValueMr = tmpMd.MakeHostInstanceGeneric(base.Session, objectGit); + + /* Stubs generate Method(Writer writer, T value). */ + MethodDefinition createdWriterMd = CreateStaticWriterStubMethodDefinition(objectTr); + AddToStaticWriters(objectTr, createdWriterMd); + + ILProcessor processor = createdWriterMd.Body.GetILProcessor(); + + //Value parameter. + ParameterDefinition valuePd = createdWriterMd.Parameters[1]; + ParameterDefinition writerPd = createdWriterMd.Parameters[0]; + + //Have to write a new ret on null because nullables use hasValue for null checks. + Instruction afterNullRetInst = processor.Create(OpCodes.Nop); + processor.Emit(OpCodes.Ldarga, valuePd); + processor.Emit(OpCodes.Call, genericHasValueMr); + processor.Emit(OpCodes.Brtrue_S, afterNullRetInst); + wh.CreateWriteBool(processor, writerPd, true); + processor.Emit(OpCodes.Ret); + processor.Append(afterNullRetInst); + + //Code will only execute here and below if not null. + wh.CreateWriteBool(processor, writerPd, false); + + processor.Emit(OpCodes.Ldarg, writerPd); + processor.Emit(OpCodes.Ldarga, valuePd); + processor.Emit(OpCodes.Call, genericGetValueMr); + //If an auto pack method then insert default value. + if (wh.IsAutoPackedType(valueTr)) + { + AutoPackType packType = base.GetClass().GetDefaultAutoPackType(valueTr); + processor.Emit(OpCodes.Ldc_I4, (int)packType); + } + processor.Emit(OpCodes.Call, valueWriterMr); + + processor.Emit(OpCodes.Ret); + return base.ImportReference(createdWriterMd); + } + + + /// + /// Creates a writer for a class or struct of objectTypeRef. + /// + /// + /// + private MethodReference CreateClassOrStructWriterMethodDefinition(TypeReference objectTr) + { + WriterProcessor wh = base.GetClass(); + + /*Stubs generate Method(Writer writer, T value). */ + MethodDefinition createdWriterMd = CreateStaticWriterStubMethodDefinition(objectTr); + AddToStaticWriters(objectTr, createdWriterMd); + ILProcessor processor = createdWriterMd.Body.GetILProcessor(); + + //If not a value type then add a null check. + if (!objectTr.CachedResolve(base.Session).IsValueType) + { + ParameterDefinition writerPd = createdWriterMd.Parameters[0]; + wh.CreateRetOnNull(processor, writerPd, createdWriterMd.Parameters[1], true); + //Code will only execute here and below if not null. + wh.CreateWriteBool(processor, writerPd, false); + } + + //Write all fields for the class or struct. + ParameterDefinition valueParameterDef = createdWriterMd.Parameters[1]; + if (!WriteFieldsAndProperties(createdWriterMd, valueParameterDef, objectTr)) + return null; + + processor.Emit(OpCodes.Ret); + return base.ImportReference(createdWriterMd); + } + + /// + /// Find all fields in type and write them + /// + /// + /// + /// false if fail + private bool WriteFieldsAndProperties(MethodDefinition generatedWriteMd, ParameterDefinition valuePd, TypeReference objectTr) + { + WriterProcessor wh = base.GetClass(); + + //This probably isn't needed but I'm too afraid to remove it. + if (objectTr.Module != base.Module) + objectTr = base.ImportReference(objectTr.CachedResolve(base.Session)); + + //Fields + foreach (FieldDefinition fieldDef in objectTr.FindAllSerializableFields(base.Session))//, WriterHelper.EXCLUDED_AUTO_SERIALIZER_TYPES)) + { + TypeReference tr; + if (fieldDef.FieldType.IsGenericInstance) + { + GenericInstanceType genericTr = (GenericInstanceType)fieldDef.FieldType; + tr = genericTr.GenericArguments[0]; + } + else + { + tr = fieldDef.FieldType; + } + if (GetWriteMethod(fieldDef.FieldType, out MethodReference writeMr)) + wh.CreateWrite(generatedWriteMd, valuePd, fieldDef, writeMr); + } + + //Properties. + foreach (PropertyDefinition propertyDef in objectTr.FindAllSerializableProperties(base.Session + , WriterProcessor.EXCLUDED_AUTO_SERIALIZER_TYPES, WriterProcessor.EXCLUDED_ASSEMBLY_PREFIXES)) + { + if (GetWriteMethod(propertyDef.PropertyType, out MethodReference writerMr)) + { + MethodReference getMr = base.Module.ImportReference(propertyDef.GetMethod); + wh.CreateWrite(generatedWriteMd, valuePd, getMr, writerMr); + } + } + + //Gets or creates writer method and outputs it. Returns true if method is found or created. + bool GetWriteMethod(TypeReference tr, out MethodReference writeMr) + { + tr = base.ImportReference(tr); + writeMr = wh.GetOrCreateWriteMethodReference(tr); + return (writeMr != null); + } + + return true; + } + + + /// + /// Creates a writer for an enum. + /// + /// + /// + private MethodReference CreateEnumWriterMethodDefinition(TypeReference enumTr) + { + WriterProcessor wh = base.GetClass(); + + MethodDefinition createdWriterMd = CreateStaticWriterStubMethodDefinition(enumTr); + AddToStaticWriters(enumTr, createdWriterMd); + + ILProcessor processor = createdWriterMd.Body.GetILProcessor(); + + //Element type for enum. EG: byte int ect + TypeReference underlyingTypeRef = enumTr.CachedResolve(base.Session).GetEnumUnderlyingTypeReference(); + //Method to write that type. + MethodReference underlyingWriterMethodRef = wh.GetOrCreateWriteMethodReference(underlyingTypeRef); + if (underlyingWriterMethodRef == null) + return null; + + ParameterDefinition writerParameterDef = createdWriterMd.Parameters[0]; + ParameterDefinition valueParameterDef = createdWriterMd.Parameters[1]; + //Push writer and value into call. + processor.Emit(OpCodes.Ldarg, writerParameterDef); + processor.Emit(OpCodes.Ldarg, valueParameterDef); + if (wh.IsAutoPackedType(underlyingTypeRef)) + processor.Emit(OpCodes.Ldc_I4, (int)AutoPackType.Packed); + + //writer.WriteXXX(value) + processor.Emit(OpCodes.Call, underlyingWriterMethodRef); + + processor.Emit(OpCodes.Ret); + return base.ImportReference(createdWriterMd); + } + + /// + /// Calls an instanced writer from a static writer. + /// + private void CallInstancedWriter(MethodDefinition staticWriterMd, MethodReference instancedWriterMr) + { + ParameterDefinition writerPd = staticWriterMd.Parameters[0]; + ParameterDefinition valuePd = staticWriterMd.Parameters[1]; + ILProcessor processor = staticWriterMd.Body.GetILProcessor(); + processor.Emit(OpCodes.Ldarg, writerPd); + processor.Emit(OpCodes.Ldarg, valuePd); + processor.Emit(instancedWriterMr.GetCallOpCode(base.Session), instancedWriterMr); + processor.Emit(OpCodes.Ret); + } + + /// + /// Creates a writer for an array. + /// + private MethodReference CreateArrayWriterMethodReference(TypeReference objectTr) + { + WriterImports wi = base.GetClass(); + TypeReference valueTr = objectTr.GetElementType(); + + //Write not found. + if (GetOrCreateWriteMethodReference(valueTr) == null) + return null; + + MethodDefinition createdMd = CreateStaticWriterStubMethodDefinition(objectTr); + AddToStaticWriters(objectTr, createdMd); + + //Find instanced writer to use. + MethodReference instancedWriteMr = wi.Writer_WriteArray_MethodRef; + //Make generic. + GenericInstanceMethod writeGim = instancedWriteMr.MakeGenericMethod(new TypeReference[] { valueTr }); + CallInstancedWriter(createdMd, writeGim); + + return base.ImportReference(createdMd); + } + + /// + /// Creates a writer for a variety of generic collections. + /// + private MethodReference CreateGenericCollectionWriterMethodReference(TypeReference objectTr, SerializerType st) + { + WriterImports wi = base.GetClass(); + //Make value field generic. + GenericInstanceType genericInstance = (GenericInstanceType)objectTr; + base.ImportReference(genericInstance); + TypeReference valueTr = genericInstance.GenericArguments[0]; + + List genericArguments = new List(); + //Make sure all arguments have writers. + foreach (TypeReference gaTr in genericInstance.GenericArguments) + { + MethodReference mr = GetOrCreateWriteMethodReference(gaTr); + //Writer not found. + if (mr == null) + { + base.LogError($"Writer could not be found or created for type {gaTr.FullName}."); + return null; + } + + genericArguments.Add(gaTr); + } + MethodReference valueWriteMr = GetOrCreateWriteMethodReference(valueTr); + if (valueWriteMr == null) + return null; + + MethodDefinition createdMd = CreateStaticWriterStubMethodDefinition(objectTr); + AddToStaticWriters(objectTr, createdMd); + + //Find instanced writer to use. + MethodReference instancedWriteMr; + if (st == SerializerType.Dictionary) + instancedWriteMr = wi.Writer_WriteDictionary_MethodRef; + else if (st == SerializerType.List) + instancedWriteMr = wi.Writer_WriteList_MethodRef; + else if (st == SerializerType.ListCache) + instancedWriteMr = wi.Writer_WriteListCache_MethodRef; + else + instancedWriteMr = null; + + //Not found. + if (instancedWriteMr == null) + { + base.LogError($"Instanced writer not found for SerializerType {st} on object {objectTr.Name}."); + return null; + } + + //Make generic. + GenericInstanceMethod writeGim = instancedWriteMr.MakeGenericMethod(genericArguments.ToArray()); + CallInstancedWriter(createdMd, writeGim); + + return base.ImportReference(createdMd); + } + + /// + /// Creates a method definition stub for objectTypeRef. + /// + /// + /// + public MethodDefinition CreateStaticWriterStubMethodDefinition(TypeReference objectTypeRef, string nameExtension = WriterProcessor.GENERATED_WRITER_NAMESPACE) + { + string methodName = $"{GENERATED_WRITE_PREFIX}{objectTypeRef.FullName}{nameExtension}"; + // create new writer for this type + TypeDefinition writerTypeDef = base.GetClass().GetOrCreateClass(out _, GENERATED_TYPE_ATTRIBUTES, GENERATED_WRITERS_CLASS_NAME, null); + + MethodDefinition writerMethodDef = writerTypeDef.AddMethod(methodName, + MethodAttributes.Public | + MethodAttributes.Static | + MethodAttributes.HideBySig); + + base.GetClass().CreateParameter(writerMethodDef, base.GetClass().Writer_TypeRef, "writer"); + base.GetClass().CreateParameter(writerMethodDef, objectTypeRef, "value"); + base.GetClass().MakeExtensionMethod(writerMethodDef); + writerMethodDef.Body.InitLocals = true; + + return writerMethodDef; + } + #endregion + + } +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Processing/WriterProcessor.cs.meta b/Assets/FishNet/CodeGenerating/Processing/WriterProcessor.cs.meta new file mode 100644 index 0000000..9ba1d8a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/WriterProcessor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2a4021bd44dc40f47abb494e0a4326f9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Unity.FishNet.CodeGen.asmdef b/Assets/FishNet/CodeGenerating/Unity.FishNet.CodeGen.asmdef new file mode 100644 index 0000000..9e678c5 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Unity.FishNet.CodeGen.asmdef @@ -0,0 +1,19 @@ +{ + "name": "Unity.FishNet.Codegen", + "references": [ + "FishNet.Runtime", + "FishNet.Codegen.Cecil", + "FishNet.Generated" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": true, + "precompiledReferences": [], + "autoReferenced": false, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Unity.FishNet.CodeGen.asmdef.meta b/Assets/FishNet/CodeGenerating/Unity.FishNet.CodeGen.asmdef.meta new file mode 100644 index 0000000..a6d9d76 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Unity.FishNet.CodeGen.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9341dc36b33c3984e97b22dac619ca50 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4.meta new file mode 100644 index 0000000..ec9a868 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7f482f18100f20045bd2188d839d9217 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Directory.Build.props b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Directory.Build.props new file mode 100644 index 0000000..ee63f7a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Directory.Build.props @@ -0,0 +1,30 @@ + + + false + false + false + Debug;Release + true + true + $(MSBuildThisFileDirectory)\cecil.snk + $(DefineConstants);NET_CORE + + + + true + + + + + + + + + + + + + $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), Mono.Cecil.overrides))\Mono.Cecil.overrides + + + diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Directory.Build.props.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Directory.Build.props.meta new file mode 100644 index 0000000..6f127c1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Directory.Build.props.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c3a066bef0608d24987201601e20a905 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/LICENSE.txt b/Assets/FishNet/CodeGenerating/cecil-0.11.4/LICENSE.txt new file mode 100644 index 0000000..afd0ae6 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/LICENSE.txt @@ -0,0 +1,21 @@ +Copyright (c) 2008 - 2015 Jb Evain +Copyright (c) 2008 - 2011 Novell, Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/LICENSE.txt.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/LICENSE.txt.meta new file mode 100644 index 0000000..da1734e --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/LICENSE.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 65323af257ddec3409ed36503b853604 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil.meta new file mode 100644 index 0000000..c287230 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 952f0fa3545cde844afce313f2b2f3b9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Code.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Code.cs new file mode 100644 index 0000000..ce5c6e0 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Code.cs @@ -0,0 +1,234 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil.Cil { + + public enum Code { + Nop, + Break, + Ldarg_0, + Ldarg_1, + Ldarg_2, + Ldarg_3, + Ldloc_0, + Ldloc_1, + Ldloc_2, + Ldloc_3, + Stloc_0, + Stloc_1, + Stloc_2, + Stloc_3, + Ldarg_S, + Ldarga_S, + Starg_S, + Ldloc_S, + Ldloca_S, + Stloc_S, + Ldnull, + Ldc_I4_M1, + Ldc_I4_0, + Ldc_I4_1, + Ldc_I4_2, + Ldc_I4_3, + Ldc_I4_4, + Ldc_I4_5, + Ldc_I4_6, + Ldc_I4_7, + Ldc_I4_8, + Ldc_I4_S, + Ldc_I4, + Ldc_I8, + Ldc_R4, + Ldc_R8, + Dup, + Pop, + Jmp, + Call, + Calli, + Ret, + Br_S, + Brfalse_S, + Brtrue_S, + Beq_S, + Bge_S, + Bgt_S, + Ble_S, + Blt_S, + Bne_Un_S, + Bge_Un_S, + Bgt_Un_S, + Ble_Un_S, + Blt_Un_S, + Br, + Brfalse, + Brtrue, + Beq, + Bge, + Bgt, + Ble, + Blt, + Bne_Un, + Bge_Un, + Bgt_Un, + Ble_Un, + Blt_Un, + Switch, + Ldind_I1, + Ldind_U1, + Ldind_I2, + Ldind_U2, + Ldind_I4, + Ldind_U4, + Ldind_I8, + Ldind_I, + Ldind_R4, + Ldind_R8, + Ldind_Ref, + Stind_Ref, + Stind_I1, + Stind_I2, + Stind_I4, + Stind_I8, + Stind_R4, + Stind_R8, + Add, + Sub, + Mul, + Div, + Div_Un, + Rem, + Rem_Un, + And, + Or, + Xor, + Shl, + Shr, + Shr_Un, + Neg, + Not, + Conv_I1, + Conv_I2, + Conv_I4, + Conv_I8, + Conv_R4, + Conv_R8, + Conv_U4, + Conv_U8, + Callvirt, + Cpobj, + Ldobj, + Ldstr, + Newobj, + Castclass, + Isinst, + Conv_R_Un, + Unbox, + Throw, + Ldfld, + Ldflda, + Stfld, + Ldsfld, + Ldsflda, + Stsfld, + Stobj, + Conv_Ovf_I1_Un, + Conv_Ovf_I2_Un, + Conv_Ovf_I4_Un, + Conv_Ovf_I8_Un, + Conv_Ovf_U1_Un, + Conv_Ovf_U2_Un, + Conv_Ovf_U4_Un, + Conv_Ovf_U8_Un, + Conv_Ovf_I_Un, + Conv_Ovf_U_Un, + Box, + Newarr, + Ldlen, + Ldelema, + Ldelem_I1, + Ldelem_U1, + Ldelem_I2, + Ldelem_U2, + Ldelem_I4, + Ldelem_U4, + Ldelem_I8, + Ldelem_I, + Ldelem_R4, + Ldelem_R8, + Ldelem_Ref, + Stelem_I, + Stelem_I1, + Stelem_I2, + Stelem_I4, + Stelem_I8, + Stelem_R4, + Stelem_R8, + Stelem_Ref, + Ldelem_Any, + Stelem_Any, + Unbox_Any, + Conv_Ovf_I1, + Conv_Ovf_U1, + Conv_Ovf_I2, + Conv_Ovf_U2, + Conv_Ovf_I4, + Conv_Ovf_U4, + Conv_Ovf_I8, + Conv_Ovf_U8, + Refanyval, + Ckfinite, + Mkrefany, + Ldtoken, + Conv_U2, + Conv_U1, + Conv_I, + Conv_Ovf_I, + Conv_Ovf_U, + Add_Ovf, + Add_Ovf_Un, + Mul_Ovf, + Mul_Ovf_Un, + Sub_Ovf, + Sub_Ovf_Un, + Endfinally, + Leave, + Leave_S, + Stind_I, + Conv_U, + Arglist, + Ceq, + Cgt, + Cgt_Un, + Clt, + Clt_Un, + Ldftn, + Ldvirtftn, + Ldarg, + Ldarga, + Starg, + Ldloc, + Ldloca, + Stloc, + Localloc, + Endfilter, + Unaligned, + Volatile, + Tail, + Initobj, + Constrained, + Cpblk, + Initblk, + No, + Rethrow, + Sizeof, + Refanytype, + Readonly, + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Code.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Code.cs.meta new file mode 100644 index 0000000..087491a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Code.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2619210c5ef352b4aac70d8e5fab7a43 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/CodeReader.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/CodeReader.cs new file mode 100644 index 0000000..206b49a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/CodeReader.cs @@ -0,0 +1,663 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.PE; +using MonoFN.Collections.Generic; +using System; + +namespace MonoFN.Cecil.Cil { + + sealed class CodeReader : BinaryStreamReader { + + readonly internal MetadataReader reader; + + int start; + + MethodDefinition method; + MethodBody body; + + int Offset { + get { return Position - start; } + } + + public CodeReader (MetadataReader reader) + : base (reader.image.Stream.value) + { + this.reader = reader; + } + + public int MoveTo (MethodDefinition method) + { + this.method = method; + this.reader.context = method; + var position = this.Position; + this.Position = (int)reader.image.ResolveVirtualAddress ((uint)method.RVA); + return position; + } + + public void MoveBackTo (int position) + { + this.reader.context = null; + this.Position = position; + } + + public MethodBody ReadMethodBody (MethodDefinition method) + { + var position = MoveTo (method); + this.body = new MethodBody (method); + + ReadMethodBody (); + + MoveBackTo (position); + return this.body; + } + + public int ReadCodeSize (MethodDefinition method) + { + var position = MoveTo (method); + + var code_size = ReadCodeSize (); + + MoveBackTo (position); + return code_size; + } + + int ReadCodeSize () + { + var flags = ReadByte (); + switch (flags & 0x3) { + case 0x2: // tiny + return flags >> 2; + case 0x3: // fat + Advance (-1 + 2 + 2); // go back, 2 bytes flags, 2 bytes stack size + return (int)ReadUInt32 (); + default: + throw new InvalidOperationException (); + } + } + + void ReadMethodBody () + { + var flags = ReadByte (); + switch (flags & 0x3) { + case 0x2: // tiny + body.code_size = flags >> 2; + body.MaxStackSize = 8; + ReadCode (); + break; + case 0x3: // fat + Advance (-1); + ReadFatMethod (); + break; + default: + throw new InvalidOperationException (); + } + + var symbol_reader = reader.module.symbol_reader; + + if (symbol_reader != null && method.debug_info == null) + method.debug_info = symbol_reader.Read (method); + + if (method.debug_info != null) + ReadDebugInfo (); + } + + void ReadFatMethod () + { + var flags = ReadUInt16 (); + body.max_stack_size = ReadUInt16 (); + body.code_size = (int)ReadUInt32 (); + body.local_var_token = new MetadataToken (ReadUInt32 ()); + body.init_locals = (flags & 0x10) != 0; + + if (body.local_var_token.RID != 0) + body.variables = ReadVariables (body.local_var_token); + + ReadCode (); + + if ((flags & 0x8) != 0) + ReadSection (); + } + + public VariableDefinitionCollection ReadVariables (MetadataToken local_var_token) + { + var position = reader.position; + var variables = reader.ReadVariables (local_var_token, method); + reader.position = position; + + return variables; + } + + void ReadCode () + { + start = Position; + var code_size = body.code_size; + + if (code_size < 0 || Length <= (uint)(code_size + Position)) + code_size = 0; + + var end = start + code_size; + var instructions = body.instructions = new InstructionCollection (method, (code_size + 1) / 2); + + while (Position < end) { + var offset = Position - start; + var opcode = ReadOpCode (); + var current = new Instruction (offset, opcode); + + if (opcode.OperandType != OperandType.InlineNone) + current.operand = ReadOperand (current); + + instructions.Add (current); + } + + ResolveBranches (instructions); + } + + OpCode ReadOpCode () + { + var il_opcode = ReadByte (); + return il_opcode != 0xfe + ? OpCodes.OneByteOpCode [il_opcode] + : OpCodes.TwoBytesOpCode [ReadByte ()]; + } + + object ReadOperand (Instruction instruction) + { + switch (instruction.opcode.OperandType) { + case OperandType.InlineSwitch: + var length = ReadInt32 (); + var base_offset = Offset + (4 * length); + var branches = new int [length]; + for (int i = 0; i < length; i++) + branches [i] = base_offset + ReadInt32 (); + return branches; + case OperandType.ShortInlineBrTarget: + return ReadSByte () + Offset; + case OperandType.InlineBrTarget: + return ReadInt32 () + Offset; + case OperandType.ShortInlineI: + if (instruction.opcode == OpCodes.Ldc_I4_S) + return ReadSByte (); + + return ReadByte (); + case OperandType.InlineI: + return ReadInt32 (); + case OperandType.ShortInlineR: + return ReadSingle (); + case OperandType.InlineR: + return ReadDouble (); + case OperandType.InlineI8: + return ReadInt64 (); + case OperandType.ShortInlineVar: + return GetVariable (ReadByte ()); + case OperandType.InlineVar: + return GetVariable (ReadUInt16 ()); + case OperandType.ShortInlineArg: + return GetParameter (ReadByte ()); + case OperandType.InlineArg: + return GetParameter (ReadUInt16 ()); + case OperandType.InlineSig: + return GetCallSite (ReadToken ()); + case OperandType.InlineString: + return GetString (ReadToken ()); + case OperandType.InlineTok: + case OperandType.InlineType: + case OperandType.InlineMethod: + case OperandType.InlineField: + return reader.LookupToken (ReadToken ()); + default: + throw new NotSupportedException (); + } + } + + public string GetString (MetadataToken token) + { + return reader.image.UserStringHeap.Read (token.RID); + } + + public ParameterDefinition GetParameter (int index) + { + return body.GetParameter (index); + } + + public VariableDefinition GetVariable (int index) + { + return body.GetVariable (index); + } + + public CallSite GetCallSite (MetadataToken token) + { + return reader.ReadCallSite (token); + } + + void ResolveBranches (Collection instructions) + { + var items = instructions.items; + var size = instructions.size; + + for (int i = 0; i < size; i++) { + var instruction = items [i]; + switch (instruction.opcode.OperandType) { + case OperandType.ShortInlineBrTarget: + case OperandType.InlineBrTarget: + instruction.operand = GetInstruction ((int)instruction.operand); + break; + case OperandType.InlineSwitch: + var offsets = (int [])instruction.operand; + var branches = new Instruction [offsets.Length]; + for (int j = 0; j < offsets.Length; j++) + branches [j] = GetInstruction (offsets [j]); + + instruction.operand = branches; + break; + } + } + } + + Instruction GetInstruction (int offset) + { + return GetInstruction (body.Instructions, offset); + } + + static Instruction GetInstruction (Collection instructions, int offset) + { + var size = instructions.size; + var items = instructions.items; + if (offset < 0 || offset > items [size - 1].offset) + return null; + + int min = 0; + int max = size - 1; + while (min <= max) { + int mid = min + ((max - min) / 2); + var instruction = items [mid]; + var instruction_offset = instruction.offset; + + if (offset == instruction_offset) + return instruction; + + if (offset < instruction_offset) + max = mid - 1; + else + min = mid + 1; + } + + return null; + } + + void ReadSection () + { + Align (4); + + const byte fat_format = 0x40; + const byte more_sects = 0x80; + + var flags = ReadByte (); + if ((flags & fat_format) == 0) + ReadSmallSection (); + else + ReadFatSection (); + + if ((flags & more_sects) != 0) + ReadSection (); + } + + void ReadSmallSection () + { + var count = ReadByte () / 12; + Advance (2); + + ReadExceptionHandlers ( + count, + () => (int)ReadUInt16 (), + () => (int)ReadByte ()); + } + + void ReadFatSection () + { + Advance (-1); + var count = (ReadInt32 () >> 8) / 24; + + ReadExceptionHandlers ( + count, + ReadInt32, + ReadInt32); + } + + // inline ? + void ReadExceptionHandlers (int count, Func read_entry, Func read_length) + { + for (int i = 0; i < count; i++) { + var handler = new ExceptionHandler ( + (ExceptionHandlerType)(read_entry () & 0x7)); + + handler.TryStart = GetInstruction (read_entry ()); + handler.TryEnd = GetInstruction (handler.TryStart.Offset + read_length ()); + + handler.HandlerStart = GetInstruction (read_entry ()); + handler.HandlerEnd = GetInstruction (handler.HandlerStart.Offset + read_length ()); + + ReadExceptionHandlerSpecific (handler); + + this.body.ExceptionHandlers.Add (handler); + } + } + + void ReadExceptionHandlerSpecific (ExceptionHandler handler) + { + switch (handler.HandlerType) { + case ExceptionHandlerType.Catch: + handler.CatchType = (TypeReference)reader.LookupToken (ReadToken ()); + break; + case ExceptionHandlerType.Filter: + handler.FilterStart = GetInstruction (ReadInt32 ()); + break; + default: + Advance (4); + break; + } + } + + public MetadataToken ReadToken () + { + return new MetadataToken (ReadUInt32 ()); + } + + void ReadDebugInfo () + { + if (method.debug_info.sequence_points != null) + ReadSequencePoints (); + + if (method.debug_info.scope != null) + ReadScope (method.debug_info.scope); + + if (method.custom_infos != null) + ReadCustomDebugInformations (method); + } + + void ReadCustomDebugInformations (MethodDefinition method) + { + var custom_infos = method.custom_infos; + + for (int i = 0; i < custom_infos.Count; i++) { + var state_machine_scope = custom_infos [i] as StateMachineScopeDebugInformation; + if (state_machine_scope != null) + ReadStateMachineScope (state_machine_scope); + + var async_method = custom_infos [i] as AsyncMethodBodyDebugInformation; + if (async_method != null) + ReadAsyncMethodBody (async_method); + } + } + + void ReadAsyncMethodBody (AsyncMethodBodyDebugInformation async_method) + { + if (async_method.catch_handler.Offset > -1) + async_method.catch_handler = new InstructionOffset (GetInstruction (async_method.catch_handler.Offset)); + + if (!async_method.yields.IsNullOrEmpty ()) + for (int i = 0; i < async_method.yields.Count; i++) + async_method.yields [i] = new InstructionOffset (GetInstruction (async_method.yields [i].Offset)); + + if (!async_method.resumes.IsNullOrEmpty ()) + for (int i = 0; i < async_method.resumes.Count; i++) + async_method.resumes [i] = new InstructionOffset (GetInstruction (async_method.resumes [i].Offset)); + } + + void ReadStateMachineScope (StateMachineScopeDebugInformation state_machine_scope) + { + if (state_machine_scope.scopes.IsNullOrEmpty ()) + return; + + foreach (var scope in state_machine_scope.scopes) { + scope.start = new InstructionOffset (GetInstruction (scope.start.Offset)); + + var end_instruction = GetInstruction (scope.end.Offset); + scope.end = end_instruction == null + ? new InstructionOffset () + : new InstructionOffset (end_instruction); + } + } + + void ReadSequencePoints () + { + var symbol = method.debug_info; + + for (int i = 0; i < symbol.sequence_points.Count; i++) { + var sequence_point = symbol.sequence_points [i]; + var instruction = GetInstruction (sequence_point.Offset); + if (instruction != null) + sequence_point.offset = new InstructionOffset (instruction); + } + } + + void ReadScopes (Collection scopes) + { + for (int i = 0; i < scopes.Count; i++) + ReadScope (scopes [i]); + } + + void ReadScope (ScopeDebugInformation scope) + { + var start_instruction = GetInstruction (scope.Start.Offset); + if (start_instruction != null) + scope.Start = new InstructionOffset (start_instruction); + + var end_instruction = GetInstruction (scope.End.Offset); + scope.End = end_instruction != null + ? new InstructionOffset (end_instruction) + : new InstructionOffset (); + + if (!scope.variables.IsNullOrEmpty ()) { + for (int i = 0; i < scope.variables.Count; i++) { + var variable_info = scope.variables [i]; + var variable = GetVariable (variable_info.Index); + if (variable != null) + variable_info.index = new VariableIndex (variable); + } + } + + if (!scope.scopes.IsNullOrEmpty ()) + ReadScopes (scope.scopes); + } + + public ByteBuffer PatchRawMethodBody (MethodDefinition method, CodeWriter writer, out int code_size, out MetadataToken local_var_token) + { + var position = MoveTo (method); + + var buffer = new ByteBuffer (); + + var flags = ReadByte (); + + switch (flags & 0x3) { + case 0x2: // tiny + buffer.WriteByte (flags); + local_var_token = MetadataToken.Zero; + code_size = flags >> 2; + PatchRawCode (buffer, code_size, writer); + break; + case 0x3: // fat + Advance (-1); + PatchRawFatMethod (buffer, writer, out code_size, out local_var_token); + break; + default: + throw new NotSupportedException (); + } + + MoveBackTo (position); + + return buffer; + } + + void PatchRawFatMethod (ByteBuffer buffer, CodeWriter writer, out int code_size, out MetadataToken local_var_token) + { + var flags = ReadUInt16 (); + buffer.WriteUInt16 (flags); + buffer.WriteUInt16 (ReadUInt16 ()); + code_size = ReadInt32 (); + buffer.WriteInt32 (code_size); + local_var_token = ReadToken (); + + if (local_var_token.RID > 0) { + var variables = ReadVariables (local_var_token); + buffer.WriteUInt32 (variables != null + ? writer.GetStandAloneSignature (variables).ToUInt32 () + : 0); + } else + buffer.WriteUInt32 (0); + + PatchRawCode (buffer, code_size, writer); + + if ((flags & 0x8) != 0) + PatchRawSection (buffer, writer.metadata); + } + + void PatchRawCode (ByteBuffer buffer, int code_size, CodeWriter writer) + { + var metadata = writer.metadata; + buffer.WriteBytes (ReadBytes (code_size)); + var end = buffer.position; + buffer.position -= code_size; + + while (buffer.position < end) { + OpCode opcode; + var il_opcode = buffer.ReadByte (); + if (il_opcode != 0xfe) { + opcode = OpCodes.OneByteOpCode [il_opcode]; + } else { + var il_opcode2 = buffer.ReadByte (); + opcode = OpCodes.TwoBytesOpCode [il_opcode2]; + } + + switch (opcode.OperandType) { + case OperandType.ShortInlineI: + case OperandType.ShortInlineBrTarget: + case OperandType.ShortInlineVar: + case OperandType.ShortInlineArg: + buffer.position += 1; + break; + case OperandType.InlineVar: + case OperandType.InlineArg: + buffer.position += 2; + break; + case OperandType.InlineBrTarget: + case OperandType.ShortInlineR: + case OperandType.InlineI: + buffer.position += 4; + break; + case OperandType.InlineI8: + case OperandType.InlineR: + buffer.position += 8; + break; + case OperandType.InlineSwitch: + var length = buffer.ReadInt32 (); + buffer.position += length * 4; + break; + case OperandType.InlineString: + var @string = GetString (new MetadataToken (buffer.ReadUInt32 ())); + buffer.position -= 4; + buffer.WriteUInt32 ( + new MetadataToken ( + TokenType.String, + metadata.user_string_heap.GetStringIndex (@string)).ToUInt32 ()); + break; + case OperandType.InlineSig: + var call_site = GetCallSite (new MetadataToken (buffer.ReadUInt32 ())); + buffer.position -= 4; + buffer.WriteUInt32 (writer.GetStandAloneSignature (call_site).ToUInt32 ()); + break; + case OperandType.InlineTok: + case OperandType.InlineType: + case OperandType.InlineMethod: + case OperandType.InlineField: + var provider = reader.LookupToken (new MetadataToken (buffer.ReadUInt32 ())); + buffer.position -= 4; + buffer.WriteUInt32 (metadata.LookupToken (provider).ToUInt32 ()); + break; + } + } + } + + void PatchRawSection (ByteBuffer buffer, MetadataBuilder metadata) + { + var position = Position; + Align (4); + buffer.WriteBytes (Position - position); + + const byte fat_format = 0x40; + const byte more_sects = 0x80; + + var flags = ReadByte (); + if ((flags & fat_format) == 0) { + buffer.WriteByte (flags); + PatchRawSmallSection (buffer, metadata); + } else + PatchRawFatSection (buffer, metadata); + + if ((flags & more_sects) != 0) + PatchRawSection (buffer, metadata); + } + + void PatchRawSmallSection (ByteBuffer buffer, MetadataBuilder metadata) + { + var length = ReadByte (); + buffer.WriteByte (length); + Advance (2); + + buffer.WriteUInt16 (0); + + var count = length / 12; + + PatchRawExceptionHandlers (buffer, metadata, count, false); + } + + void PatchRawFatSection (ByteBuffer buffer, MetadataBuilder metadata) + { + Advance (-1); + var length = ReadInt32 (); + buffer.WriteInt32 (length); + + var count = (length >> 8) / 24; + + PatchRawExceptionHandlers (buffer, metadata, count, true); + } + + void PatchRawExceptionHandlers (ByteBuffer buffer, MetadataBuilder metadata, int count, bool fat_entry) + { + const int fat_entry_size = 16; + const int small_entry_size = 6; + + for (int i = 0; i < count; i++) { + ExceptionHandlerType handler_type; + if (fat_entry) { + var type = ReadUInt32 (); + handler_type = (ExceptionHandlerType)(type & 0x7); + buffer.WriteUInt32 (type); + } else { + var type = ReadUInt16 (); + handler_type = (ExceptionHandlerType)(type & 0x7); + buffer.WriteUInt16 (type); + } + + buffer.WriteBytes (ReadBytes (fat_entry ? fat_entry_size : small_entry_size)); + + switch (handler_type) { + case ExceptionHandlerType.Catch: + var exception = reader.LookupToken (ReadToken ()); + buffer.WriteUInt32 (metadata.LookupToken (exception).ToUInt32 ()); + break; + default: + buffer.WriteUInt32 (ReadUInt32 ()); + break; + } + } + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/CodeReader.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/CodeReader.cs.meta new file mode 100644 index 0000000..4bf35a7 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/CodeReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 451f6a2407c53554f9a16eeb62d806ce +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/CodeWriter.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/CodeWriter.cs new file mode 100644 index 0000000..e7126e8 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/CodeWriter.cs @@ -0,0 +1,651 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.Metadata; +using MonoFN.Cecil.PE; +using MonoFN.Collections.Generic; +using System; +using System.Collections.Generic; +using RVA = System.UInt32; + +namespace MonoFN.Cecil.Cil { + + sealed class CodeWriter : ByteBuffer { + + readonly RVA code_base; + internal readonly MetadataBuilder metadata; + readonly Dictionary standalone_signatures; + readonly Dictionary tiny_method_bodies; + + MethodBody body; + + public CodeWriter (MetadataBuilder metadata) + : base (0) + { + this.code_base = metadata.text_map.GetNextRVA (TextSegment.CLIHeader); + this.metadata = metadata; + this.standalone_signatures = new Dictionary (); + this.tiny_method_bodies = new Dictionary (new ByteBufferEqualityComparer ()); + } + + public RVA WriteMethodBody (MethodDefinition method) + { + RVA rva; + + if (IsUnresolved (method)) { + if (method.rva == 0) + return 0; + + rva = WriteUnresolvedMethodBody (method); + } else { + if (IsEmptyMethodBody (method.Body)) + return 0; + + rva = WriteResolvedMethodBody (method); + } + + return rva; + } + + static bool IsEmptyMethodBody (MethodBody body) + { + return body.instructions.IsNullOrEmpty () + && body.variables.IsNullOrEmpty (); + } + + static bool IsUnresolved (MethodDefinition method) + { + return method.HasBody && method.HasImage && method.body == null; + } + + RVA WriteUnresolvedMethodBody (MethodDefinition method) + { + var code_reader = metadata.module.reader.code; + + int code_size; + MetadataToken local_var_token; + var raw_body = code_reader.PatchRawMethodBody (method, this, out code_size, out local_var_token); + var fat_header = (raw_body.buffer [0] & 0x3) == 0x3; + if (fat_header) + Align (4); + + var rva = BeginMethod (); + + if (fat_header || !GetOrMapTinyMethodBody (raw_body, ref rva)) { + WriteBytes (raw_body); + } + + if (method.debug_info == null) + return rva; + + var symbol_writer = metadata.symbol_writer; + if (symbol_writer != null) { + method.debug_info.code_size = code_size; + method.debug_info.local_var_token = local_var_token; + symbol_writer.Write (method.debug_info); + } + + return rva; + } + + RVA WriteResolvedMethodBody (MethodDefinition method) + { + RVA rva; + + body = method.Body; + ComputeHeader (); + if (RequiresFatHeader ()) { + Align (4); + rva = BeginMethod (); + WriteFatHeader (); + WriteInstructions (); + + if (body.HasExceptionHandlers) + WriteExceptionHandlers (); + } else { + rva = BeginMethod (); + WriteByte ((byte)(0x2 | (body.CodeSize << 2))); // tiny + WriteInstructions (); + + var start_position = (int)(rva - code_base); + var body_size = position - start_position; + var body_bytes = new byte [body_size]; + + Array.Copy (buffer, start_position, body_bytes, 0, body_size); + + if (GetOrMapTinyMethodBody (new ByteBuffer (body_bytes), ref rva)) + position = start_position; + } + + var symbol_writer = metadata.symbol_writer; + if (symbol_writer != null && method.debug_info != null) { + method.debug_info.code_size = body.CodeSize; + method.debug_info.local_var_token = body.local_var_token; + symbol_writer.Write (method.debug_info); + } + + return rva; + } + + bool GetOrMapTinyMethodBody (ByteBuffer body, ref RVA rva) + { + RVA existing_rva; + if (tiny_method_bodies.TryGetValue (body, out existing_rva)) { + rva = existing_rva; + return true; + } + + tiny_method_bodies.Add (body, rva); + return false; + } + + void WriteFatHeader () + { + var body = this.body; + byte flags = 0x3; // fat + if (body.InitLocals) + flags |= 0x10; // init locals + if (body.HasExceptionHandlers) + flags |= 0x8; // more sections + + WriteByte (flags); + WriteByte (0x30); + WriteInt16 ((short)body.max_stack_size); + WriteInt32 (body.code_size); + body.local_var_token = body.HasVariables + ? GetStandAloneSignature (body.Variables) + : MetadataToken.Zero; + WriteMetadataToken (body.local_var_token); + } + + void WriteInstructions () + { + var instructions = body.Instructions; + var items = instructions.items; + var size = instructions.size; + + for (int i = 0; i < size; i++) { + var instruction = items [i]; + WriteOpCode (instruction.opcode); + WriteOperand (instruction); + } + } + + void WriteOpCode (OpCode opcode) + { + if (opcode.Size == 1) { + WriteByte (opcode.Op2); + } else { + WriteByte (opcode.Op1); + WriteByte (opcode.Op2); + } + } + + void WriteOperand (Instruction instruction) + { + var opcode = instruction.opcode; + var operand_type = opcode.OperandType; + if (operand_type == OperandType.InlineNone) + return; + + var operand = instruction.operand; + if (operand == null && !(operand_type == OperandType.InlineBrTarget || operand_type == OperandType.ShortInlineBrTarget)) { + throw new ArgumentException (); + } + + switch (operand_type) { + case OperandType.InlineSwitch: { + var targets = (Instruction [])operand; + WriteInt32 (targets.Length); + var diff = instruction.Offset + opcode.Size + (4 * (targets.Length + 1)); + for (int i = 0; i < targets.Length; i++) + WriteInt32 (GetTargetOffset (targets [i]) - diff); + break; + } + case OperandType.ShortInlineBrTarget: { + var target = (Instruction)operand; + var offset = target != null ? GetTargetOffset (target) : body.code_size; + WriteSByte ((sbyte)(offset - (instruction.Offset + opcode.Size + 1))); + break; + } + case OperandType.InlineBrTarget: { + var target = (Instruction)operand; + var offset = target != null ? GetTargetOffset (target) : body.code_size; + WriteInt32 (offset - (instruction.Offset + opcode.Size + 4)); + break; + } + case OperandType.ShortInlineVar: + WriteByte ((byte)GetVariableIndex ((VariableDefinition)operand)); + break; + case OperandType.ShortInlineArg: + WriteByte ((byte)GetParameterIndex ((ParameterDefinition)operand)); + break; + case OperandType.InlineVar: + WriteInt16 ((short)GetVariableIndex ((VariableDefinition)operand)); + break; + case OperandType.InlineArg: + WriteInt16 ((short)GetParameterIndex ((ParameterDefinition)operand)); + break; + case OperandType.InlineSig: + WriteMetadataToken (GetStandAloneSignature ((CallSite)operand)); + break; + case OperandType.ShortInlineI: + if (opcode == OpCodes.Ldc_I4_S) + WriteSByte ((sbyte)operand); + else + WriteByte ((byte)operand); + break; + case OperandType.InlineI: + WriteInt32 ((int)operand); + break; + case OperandType.InlineI8: + WriteInt64 ((long)operand); + break; + case OperandType.ShortInlineR: + WriteSingle ((float)operand); + break; + case OperandType.InlineR: + WriteDouble ((double)operand); + break; + case OperandType.InlineString: + WriteMetadataToken ( + new MetadataToken ( + TokenType.String, + GetUserStringIndex ((string)operand))); + break; + case OperandType.InlineType: + case OperandType.InlineField: + case OperandType.InlineMethod: + case OperandType.InlineTok: + WriteMetadataToken (metadata.LookupToken ((IMetadataTokenProvider)operand)); + break; + default: + throw new ArgumentException (); + } + } + + int GetTargetOffset (Instruction instruction) + { + if (instruction == null) { + var last = body.instructions [body.instructions.size - 1]; + return last.offset + last.GetSize (); + } + + return instruction.offset; + } + + uint GetUserStringIndex (string @string) + { + if (@string == null) + return 0; + + return metadata.user_string_heap.GetStringIndex (@string); + } + + static int GetVariableIndex (VariableDefinition variable) + { + return variable.Index; + } + + int GetParameterIndex (ParameterDefinition parameter) + { + if (body.method.HasThis) { + if (parameter == body.this_parameter) + return 0; + + return parameter.Index + 1; + } + + return parameter.Index; + } + + bool RequiresFatHeader () + { + var body = this.body; + return body.CodeSize >= 64 + || body.InitLocals + || body.HasVariables + || body.HasExceptionHandlers + || body.MaxStackSize > 8; + } + + void ComputeHeader () + { + int offset = 0; + var instructions = body.instructions; + var items = instructions.items; + var count = instructions.size; + var stack_size = 0; + var max_stack = 0; + Dictionary stack_sizes = null; + + if (body.HasExceptionHandlers) + ComputeExceptionHandlerStackSize (ref stack_sizes); + + for (int i = 0; i < count; i++) { + var instruction = items [i]; + instruction.offset = offset; + offset += instruction.GetSize (); + + ComputeStackSize (instruction, ref stack_sizes, ref stack_size, ref max_stack); + } + + body.code_size = offset; + body.max_stack_size = max_stack; + } + + void ComputeExceptionHandlerStackSize (ref Dictionary stack_sizes) + { + var exception_handlers = body.ExceptionHandlers; + + for (int i = 0; i < exception_handlers.Count; i++) { + var exception_handler = exception_handlers [i]; + + switch (exception_handler.HandlerType) { + case ExceptionHandlerType.Catch: + AddExceptionStackSize (exception_handler.HandlerStart, ref stack_sizes); + break; + case ExceptionHandlerType.Filter: + AddExceptionStackSize (exception_handler.FilterStart, ref stack_sizes); + AddExceptionStackSize (exception_handler.HandlerStart, ref stack_sizes); + break; + } + } + } + + static void AddExceptionStackSize (Instruction handler_start, ref Dictionary stack_sizes) + { + if (handler_start == null) + return; + + if (stack_sizes == null) + stack_sizes = new Dictionary (); + + stack_sizes [handler_start] = 1; + } + + static void ComputeStackSize (Instruction instruction, ref Dictionary stack_sizes, ref int stack_size, ref int max_stack) + { + int computed_size; + if (stack_sizes != null && stack_sizes.TryGetValue (instruction, out computed_size)) + stack_size = computed_size; + + max_stack = System.Math.Max (max_stack, stack_size); + ComputeStackDelta (instruction, ref stack_size); + max_stack = System.Math.Max (max_stack, stack_size); + + CopyBranchStackSize (instruction, ref stack_sizes, stack_size); + ComputeStackSize (instruction, ref stack_size); + } + + static void CopyBranchStackSize (Instruction instruction, ref Dictionary stack_sizes, int stack_size) + { + if (stack_size == 0) + return; + + switch (instruction.opcode.OperandType) { + case OperandType.ShortInlineBrTarget: + case OperandType.InlineBrTarget: + CopyBranchStackSize (ref stack_sizes, (Instruction)instruction.operand, stack_size); + break; + case OperandType.InlineSwitch: + var targets = (Instruction [])instruction.operand; + for (int i = 0; i < targets.Length; i++) + CopyBranchStackSize (ref stack_sizes, targets [i], stack_size); + break; + } + } + + static void CopyBranchStackSize (ref Dictionary stack_sizes, Instruction target, int stack_size) + { + if (stack_sizes == null) + stack_sizes = new Dictionary (); + + int branch_stack_size = stack_size; + + int computed_size; + if (stack_sizes.TryGetValue (target, out computed_size)) + branch_stack_size = System.Math.Max (branch_stack_size, computed_size); + + stack_sizes [target] = branch_stack_size; + } + + static void ComputeStackSize (Instruction instruction, ref int stack_size) + { + switch (instruction.opcode.FlowControl) { + case FlowControl.Branch: + case FlowControl.Throw: + case FlowControl.Return: + stack_size = 0; + break; + } + } + + static void ComputeStackDelta (Instruction instruction, ref int stack_size) + { + switch (instruction.opcode.FlowControl) { + case FlowControl.Call: { + var method = (IMethodSignature)instruction.operand; + // pop 'this' argument + if (method.HasImplicitThis () && instruction.opcode.Code != Code.Newobj) + stack_size--; + // pop normal arguments + if (method.HasParameters) + stack_size -= method.Parameters.Count; + // pop function pointer + if (instruction.opcode.Code == Code.Calli) + stack_size--; + // push return value + if (method.ReturnType.etype != ElementType.Void || instruction.opcode.Code == Code.Newobj) + stack_size++; + break; + } + default: + ComputePopDelta (instruction.opcode.StackBehaviourPop, ref stack_size); + ComputePushDelta (instruction.opcode.StackBehaviourPush, ref stack_size); + break; + } + } + + static void ComputePopDelta (StackBehaviour pop_behavior, ref int stack_size) + { + switch (pop_behavior) { + case StackBehaviour.Popi: + case StackBehaviour.Popref: + case StackBehaviour.Pop1: + stack_size--; + break; + case StackBehaviour.Pop1_pop1: + case StackBehaviour.Popi_pop1: + case StackBehaviour.Popi_popi: + case StackBehaviour.Popi_popi8: + case StackBehaviour.Popi_popr4: + case StackBehaviour.Popi_popr8: + case StackBehaviour.Popref_pop1: + case StackBehaviour.Popref_popi: + stack_size -= 2; + break; + case StackBehaviour.Popi_popi_popi: + case StackBehaviour.Popref_popi_popi: + case StackBehaviour.Popref_popi_popi8: + case StackBehaviour.Popref_popi_popr4: + case StackBehaviour.Popref_popi_popr8: + case StackBehaviour.Popref_popi_popref: + stack_size -= 3; + break; + case StackBehaviour.PopAll: + stack_size = 0; + break; + } + } + + static void ComputePushDelta (StackBehaviour push_behaviour, ref int stack_size) + { + switch (push_behaviour) { + case StackBehaviour.Push1: + case StackBehaviour.Pushi: + case StackBehaviour.Pushi8: + case StackBehaviour.Pushr4: + case StackBehaviour.Pushr8: + case StackBehaviour.Pushref: + stack_size++; + break; + case StackBehaviour.Push1_push1: + stack_size += 2; + break; + } + } + + void WriteExceptionHandlers () + { + Align (4); + + var handlers = body.ExceptionHandlers; + + if (handlers.Count < 0x15 && !RequiresFatSection (handlers)) + WriteSmallSection (handlers); + else + WriteFatSection (handlers); + } + + static bool RequiresFatSection (Collection handlers) + { + for (int i = 0; i < handlers.Count; i++) { + var handler = handlers [i]; + + if (IsFatRange (handler.TryStart, handler.TryEnd)) + return true; + + if (IsFatRange (handler.HandlerStart, handler.HandlerEnd)) + return true; + + if (handler.HandlerType == ExceptionHandlerType.Filter + && IsFatRange (handler.FilterStart, handler.HandlerStart)) + return true; + } + + return false; + } + + static bool IsFatRange (Instruction start, Instruction end) + { + if (start == null) + throw new ArgumentException (); + + if (end == null) + return true; + + return end.Offset - start.Offset > 255 || start.Offset > 65535; + } + + void WriteSmallSection (Collection handlers) + { + const byte eh_table = 0x1; + + WriteByte (eh_table); + WriteByte ((byte)(handlers.Count * 12 + 4)); + WriteBytes (2); + + WriteExceptionHandlers ( + handlers, + i => WriteUInt16 ((ushort)i), + i => WriteByte ((byte)i)); + } + + void WriteFatSection (Collection handlers) + { + const byte eh_table = 0x1; + const byte fat_format = 0x40; + + WriteByte (eh_table | fat_format); + + int size = handlers.Count * 24 + 4; + WriteByte ((byte)(size & 0xff)); + WriteByte ((byte)((size >> 8) & 0xff)); + WriteByte ((byte)((size >> 16) & 0xff)); + + WriteExceptionHandlers (handlers, WriteInt32, WriteInt32); + } + + void WriteExceptionHandlers (Collection handlers, Action write_entry, Action write_length) + { + for (int i = 0; i < handlers.Count; i++) { + var handler = handlers [i]; + + write_entry ((int)handler.HandlerType); + + write_entry (handler.TryStart.Offset); + write_length (GetTargetOffset (handler.TryEnd) - handler.TryStart.Offset); + + write_entry (handler.HandlerStart.Offset); + write_length (GetTargetOffset (handler.HandlerEnd) - handler.HandlerStart.Offset); + + WriteExceptionHandlerSpecific (handler); + } + } + + void WriteExceptionHandlerSpecific (ExceptionHandler handler) + { + switch (handler.HandlerType) { + case ExceptionHandlerType.Catch: + WriteMetadataToken (metadata.LookupToken (handler.CatchType)); + break; + case ExceptionHandlerType.Filter: + WriteInt32 (handler.FilterStart.Offset); + break; + default: + WriteInt32 (0); + break; + } + } + + public MetadataToken GetStandAloneSignature (Collection variables) + { + var signature = metadata.GetLocalVariableBlobIndex (variables); + + return GetStandAloneSignatureToken (signature); + } + + public MetadataToken GetStandAloneSignature (CallSite call_site) + { + var signature = metadata.GetCallSiteBlobIndex (call_site); + var token = GetStandAloneSignatureToken (signature); + call_site.MetadataToken = token; + return token; + } + + MetadataToken GetStandAloneSignatureToken (uint signature) + { + MetadataToken token; + if (standalone_signatures.TryGetValue (signature, out token)) + return token; + + token = new MetadataToken (TokenType.Signature, metadata.AddStandAloneSignature (signature)); + standalone_signatures.Add (signature, token); + return token; + } + + RVA BeginMethod () + { + return (RVA)(code_base + position); + } + + void WriteMetadataToken (MetadataToken token) + { + WriteUInt32 (token.ToUInt32 ()); + } + + void Align (int align) + { + align--; + WriteBytes (((position + align) & ~align) - position); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/CodeWriter.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/CodeWriter.cs.meta new file mode 100644 index 0000000..e4cccca --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/CodeWriter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 278f89c983a1bc6429c65a1d49dff091 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Document.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Document.cs new file mode 100644 index 0000000..ce30188 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Document.cs @@ -0,0 +1,123 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil.Cil { + + public enum DocumentType { + Other, + Text, + } + + public enum DocumentHashAlgorithm { + None, + MD5, + SHA1, + SHA256, + } + + public enum DocumentLanguage { + Other, + C, + Cpp, + CSharp, + Basic, + Java, + Cobol, + Pascal, + Cil, + JScript, + Smc, + MCpp, + FSharp, + } + + public enum DocumentLanguageVendor { + Other, + Microsoft, + } + + public sealed class Document : DebugInformation { + + string url; + + Guid type; + Guid hash_algorithm; + Guid language; + Guid language_vendor; + + byte [] hash; + byte [] embedded_source; + + public string Url { + get { return url; } + set { url = value; } + } + + public DocumentType Type { + get { return type.ToType (); } + set { type = value.ToGuid (); } + } + + public Guid TypeGuid { + get { return type; } + set { type = value; } + } + + public DocumentHashAlgorithm HashAlgorithm { + get { return hash_algorithm.ToHashAlgorithm (); } + set { hash_algorithm = value.ToGuid (); } + } + + public Guid HashAlgorithmGuid { + get { return hash_algorithm; } + set { hash_algorithm = value; } + } + + public DocumentLanguage Language { + get { return language.ToLanguage (); } + set { language = value.ToGuid (); } + } + + public Guid LanguageGuid { + get { return language; } + set { language = value; } + } + + public DocumentLanguageVendor LanguageVendor { + get { return language_vendor.ToVendor (); } + set { language_vendor = value.ToGuid (); } + } + + public Guid LanguageVendorGuid { + get { return language_vendor; } + set { language_vendor = value; } + } + + public byte [] Hash { + get { return hash; } + set { hash = value; } + } + + public byte [] EmbeddedSource { + get { return embedded_source; } + set { embedded_source = value; } + } + + public Document (string url) + { + this.url = url; + this.hash = Empty.Array; + this.embedded_source = Empty.Array; + this.token = new MetadataToken (TokenType.Document); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Document.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Document.cs.meta new file mode 100644 index 0000000..2a3915d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Document.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 86b3083301304a341b6059ea8c29be7a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/ExceptionHandler.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/ExceptionHandler.cs new file mode 100644 index 0000000..c017553 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/ExceptionHandler.cs @@ -0,0 +1,71 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil.Cil { + + public enum ExceptionHandlerType { + Catch = 0, + Filter = 1, + Finally = 2, + Fault = 4, + } + + public sealed class ExceptionHandler { + + Instruction try_start; + Instruction try_end; + Instruction filter_start; + Instruction handler_start; + Instruction handler_end; + + TypeReference catch_type; + ExceptionHandlerType handler_type; + + public Instruction TryStart { + get { return try_start; } + set { try_start = value; } + } + + public Instruction TryEnd { + get { return try_end; } + set { try_end = value; } + } + + public Instruction FilterStart { + get { return filter_start; } + set { filter_start = value; } + } + + public Instruction HandlerStart { + get { return handler_start; } + set { handler_start = value; } + } + + public Instruction HandlerEnd { + get { return handler_end; } + set { handler_end = value; } + } + + public TypeReference CatchType { + get { return catch_type; } + set { catch_type = value; } + } + + public ExceptionHandlerType HandlerType { + get { return handler_type; } + set { handler_type = value; } + } + + public ExceptionHandler (ExceptionHandlerType handlerType) + { + this.handler_type = handlerType; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/ExceptionHandler.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/ExceptionHandler.cs.meta new file mode 100644 index 0000000..f26784f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/ExceptionHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eb17bcae1a67ec344b65d6ed78ffb704 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/ILProcessor.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/ILProcessor.cs new file mode 100644 index 0000000..48944a2 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/ILProcessor.cs @@ -0,0 +1,291 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System; + +namespace MonoFN.Cecil.Cil { + + public sealed class ILProcessor { + + readonly MethodBody body; + readonly Collection instructions; + + public MethodBody Body { + get { return body; } + } + + internal ILProcessor (MethodBody body) + { + this.body = body; + this.instructions = body.Instructions; + } + + public Instruction Create (OpCode opcode) + { + return Instruction.Create (opcode); + } + + public Instruction Create (OpCode opcode, TypeReference type) + { + return Instruction.Create (opcode, type); + } + + public Instruction Create (OpCode opcode, CallSite site) + { + return Instruction.Create (opcode, site); + } + + public Instruction Create (OpCode opcode, MethodReference method) + { + return Instruction.Create (opcode, method); + } + + public Instruction Create (OpCode opcode, FieldReference field) + { + return Instruction.Create (opcode, field); + } + + public Instruction Create (OpCode opcode, string value) + { + return Instruction.Create (opcode, value); + } + + public Instruction Create (OpCode opcode, sbyte value) + { + return Instruction.Create (opcode, value); + } + + public Instruction Create (OpCode opcode, byte value) + { + if (opcode.OperandType == OperandType.ShortInlineVar) + return Instruction.Create (opcode, body.Variables [value]); + + if (opcode.OperandType == OperandType.ShortInlineArg) + return Instruction.Create (opcode, body.GetParameter (value)); + + return Instruction.Create (opcode, value); + } + + public Instruction Create (OpCode opcode, int value) + { + if (opcode.OperandType == OperandType.InlineVar) + return Instruction.Create (opcode, body.Variables [value]); + + if (opcode.OperandType == OperandType.InlineArg) + return Instruction.Create (opcode, body.GetParameter (value)); + + return Instruction.Create (opcode, value); + } + + public Instruction Create (OpCode opcode, long value) + { + return Instruction.Create (opcode, value); + } + + public Instruction Create (OpCode opcode, float value) + { + return Instruction.Create (opcode, value); + } + + public Instruction Create (OpCode opcode, double value) + { + return Instruction.Create (opcode, value); + } + + public Instruction Create (OpCode opcode, Instruction target) + { + return Instruction.Create (opcode, target); + } + + public Instruction Create (OpCode opcode, Instruction [] targets) + { + return Instruction.Create (opcode, targets); + } + + public Instruction Create (OpCode opcode, VariableDefinition variable) + { + return Instruction.Create (opcode, variable); + } + + public Instruction Create (OpCode opcode, ParameterDefinition parameter) + { + return Instruction.Create (opcode, parameter); + } + + public void Emit (OpCode opcode) + { + Append (Create (opcode)); + } + + public void Emit (OpCode opcode, TypeReference type) + { + Append (Create (opcode, type)); + } + + public void Emit (OpCode opcode, MethodReference method) + { + Append (Create (opcode, method)); + } + + public void Emit (OpCode opcode, CallSite site) + { + Append (Create (opcode, site)); + } + + public void Emit (OpCode opcode, FieldReference field) + { + Append (Create (opcode, field)); + } + + public void Emit (OpCode opcode, string value) + { + Append (Create (opcode, value)); + } + + public void Emit (OpCode opcode, byte value) + { + Append (Create (opcode, value)); + } + + public void Emit (OpCode opcode, sbyte value) + { + Append (Create (opcode, value)); + } + + public void Emit (OpCode opcode, int value) + { + Append (Create (opcode, value)); + } + + public void Emit (OpCode opcode, long value) + { + Append (Create (opcode, value)); + } + + public void Emit (OpCode opcode, float value) + { + Append (Create (opcode, value)); + } + + public void Emit (OpCode opcode, double value) + { + Append (Create (opcode, value)); + } + + public void Emit (OpCode opcode, Instruction target) + { + Append (Create (opcode, target)); + } + + public void Emit (OpCode opcode, Instruction [] targets) + { + Append (Create (opcode, targets)); + } + + public void Emit (OpCode opcode, VariableDefinition variable) + { + Append (Create (opcode, variable)); + } + + public void Emit (OpCode opcode, ParameterDefinition parameter) + { + Append (Create (opcode, parameter)); + } + + public void InsertBefore (Instruction target, Instruction instruction) + { + if (target == null) + throw new ArgumentNullException ("target"); + if (instruction == null) + throw new ArgumentNullException ("instruction"); + + var index = instructions.IndexOf (target); + if (index == -1) + throw new ArgumentOutOfRangeException ("target"); + + instructions.Insert (index, instruction); + } + + public void InsertAfter (Instruction target, Instruction instruction) + { + if (target == null) + throw new ArgumentNullException ("target"); + if (instruction == null) + throw new ArgumentNullException ("instruction"); + + var index = instructions.IndexOf (target); + if (index == -1) + throw new ArgumentOutOfRangeException ("target"); + + instructions.Insert (index + 1, instruction); + } + + public void InsertAfter (int index, Instruction instruction) + { + if (index < 0 || index >= instructions.Count) + throw new ArgumentOutOfRangeException ("index"); + if (instruction == null) + throw new ArgumentNullException ("instruction"); + + instructions.Insert (index + 1, instruction); + } + + public void Append (Instruction instruction) + { + if (instruction == null) + throw new ArgumentNullException ("instruction"); + + instructions.Add (instruction); + } + + public void Replace (Instruction target, Instruction instruction) + { + if (target == null) + throw new ArgumentNullException ("target"); + if (instruction == null) + throw new ArgumentNullException ("instruction"); + + InsertAfter (target, instruction); + Remove (target); + } + + public void Replace (int index, Instruction instruction) + { + if (instruction == null) + throw new ArgumentNullException ("instruction"); + + InsertAfter (index, instruction); + RemoveAt (index); + } + + public void Remove (Instruction instruction) + { + if (instruction == null) + throw new ArgumentNullException ("instruction"); + + if (!instructions.Remove (instruction)) + throw new ArgumentOutOfRangeException ("instruction"); + } + + public void RemoveAt (int index) + { + if (index < 0 || index >= instructions.Count) + throw new ArgumentOutOfRangeException ("index"); + + instructions.RemoveAt (index); + } + + public void Clear () + { + instructions.Clear (); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/ILProcessor.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/ILProcessor.cs.meta new file mode 100644 index 0000000..5964570 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/ILProcessor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 26902ea42064c624d82727d9ef9a8f5e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Instruction.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Instruction.cs new file mode 100644 index 0000000..93afafe --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Instruction.cs @@ -0,0 +1,296 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; +using System.Text; + +namespace MonoFN.Cecil.Cil { + + public sealed class Instruction { + + internal int offset; + internal OpCode opcode; + internal object operand; + + internal Instruction previous; + internal Instruction next; + + public int Offset { + get { return offset; } + set { offset = value; } + } + + public OpCode OpCode { + get { return opcode; } + set { opcode = value; } + } + + public object Operand { + get { return operand; } + set { operand = value; } + } + + public Instruction Previous { + get { return previous; } + set { previous = value; } + } + + public Instruction Next { + get { return next; } + set { next = value; } + } + + internal Instruction (int offset, OpCode opCode) + { + this.offset = offset; + this.opcode = opCode; + } + + internal Instruction (OpCode opcode, object operand) + { + this.opcode = opcode; + this.operand = operand; + } + + public int GetSize () + { + int size = opcode.Size; + + switch (opcode.OperandType) { + case OperandType.InlineSwitch: + return size + (1 + ((Instruction [])operand).Length) * 4; + case OperandType.InlineI8: + case OperandType.InlineR: + return size + 8; + case OperandType.InlineBrTarget: + case OperandType.InlineField: + case OperandType.InlineI: + case OperandType.InlineMethod: + case OperandType.InlineString: + case OperandType.InlineTok: + case OperandType.InlineType: + case OperandType.ShortInlineR: + case OperandType.InlineSig: + return size + 4; + case OperandType.InlineArg: + case OperandType.InlineVar: + return size + 2; + case OperandType.ShortInlineBrTarget: + case OperandType.ShortInlineI: + case OperandType.ShortInlineArg: + case OperandType.ShortInlineVar: + return size + 1; + default: + return size; + } + } + + public override string ToString () + { + var instruction = new StringBuilder (); + + AppendLabel (instruction, this); + instruction.Append (':'); + instruction.Append (' '); + instruction.Append (opcode.Name); + + if (operand == null) + return instruction.ToString (); + + instruction.Append (' '); + + switch (opcode.OperandType) { + case OperandType.ShortInlineBrTarget: + case OperandType.InlineBrTarget: + AppendLabel (instruction, (Instruction)operand); + break; + case OperandType.InlineSwitch: + var labels = (Instruction [])operand; + for (int i = 0; i < labels.Length; i++) { + if (i > 0) + instruction.Append (','); + + AppendLabel (instruction, labels [i]); + } + break; + case OperandType.InlineString: + instruction.Append ('\"'); + instruction.Append (operand); + instruction.Append ('\"'); + break; + default: + instruction.Append (operand); + break; + } + + return instruction.ToString (); + } + + static void AppendLabel (StringBuilder builder, Instruction instruction) + { + builder.Append ("IL_"); + builder.Append (instruction.offset.ToString ("x4")); + } + + public static Instruction Create (OpCode opcode) + { + if (opcode.OperandType != OperandType.InlineNone) + throw new ArgumentException ("opcode"); + + return new Instruction (opcode, null); + } + + public static Instruction Create (OpCode opcode, TypeReference type) + { + if (type == null) + throw new ArgumentNullException ("type"); + if (opcode.OperandType != OperandType.InlineType && + opcode.OperandType != OperandType.InlineTok) + throw new ArgumentException ("opcode"); + + return new Instruction (opcode, type); + } + + public static Instruction Create (OpCode opcode, CallSite site) + { + if (site == null) + throw new ArgumentNullException ("site"); + if (opcode.Code != Code.Calli) + throw new ArgumentException ("code"); + + return new Instruction (opcode, site); + } + + public static Instruction Create (OpCode opcode, MethodReference method) + { + if (method == null) + throw new ArgumentNullException ("method"); + if (opcode.OperandType != OperandType.InlineMethod && + opcode.OperandType != OperandType.InlineTok) + throw new ArgumentException ("opcode"); + + return new Instruction (opcode, method); + } + + public static Instruction Create (OpCode opcode, FieldReference field) + { + if (field == null) + throw new ArgumentNullException ("field"); + if (opcode.OperandType != OperandType.InlineField && + opcode.OperandType != OperandType.InlineTok) + throw new ArgumentException ("opcode"); + + return new Instruction (opcode, field); + } + + public static Instruction Create (OpCode opcode, string value) + { + if (value == null) + throw new ArgumentNullException ("value"); + if (opcode.OperandType != OperandType.InlineString) + throw new ArgumentException ("opcode"); + + return new Instruction (opcode, value); + } + + public static Instruction Create (OpCode opcode, sbyte value) + { + if (opcode.OperandType != OperandType.ShortInlineI && + opcode != OpCodes.Ldc_I4_S) + throw new ArgumentException ("opcode"); + + return new Instruction (opcode, value); + } + + public static Instruction Create (OpCode opcode, byte value) + { + if (opcode.OperandType != OperandType.ShortInlineI || + opcode == OpCodes.Ldc_I4_S) + throw new ArgumentException ("opcode"); + + return new Instruction (opcode, value); + } + + public static Instruction Create (OpCode opcode, int value) + { + if (opcode.OperandType != OperandType.InlineI) + throw new ArgumentException ("opcode"); + + return new Instruction (opcode, value); + } + + public static Instruction Create (OpCode opcode, long value) + { + if (opcode.OperandType != OperandType.InlineI8) + throw new ArgumentException ("opcode"); + + return new Instruction (opcode, value); + } + + public static Instruction Create (OpCode opcode, float value) + { + if (opcode.OperandType != OperandType.ShortInlineR) + throw new ArgumentException ("opcode"); + + return new Instruction (opcode, value); + } + + public static Instruction Create (OpCode opcode, double value) + { + if (opcode.OperandType != OperandType.InlineR) + throw new ArgumentException ("opcode"); + + return new Instruction (opcode, value); + } + + public static Instruction Create (OpCode opcode, Instruction target) + { + if (target == null) + throw new ArgumentNullException ("target"); + if (opcode.OperandType != OperandType.InlineBrTarget && + opcode.OperandType != OperandType.ShortInlineBrTarget) + throw new ArgumentException ("opcode"); + + return new Instruction (opcode, target); + } + + public static Instruction Create (OpCode opcode, Instruction [] targets) + { + if (targets == null) + throw new ArgumentNullException ("targets"); + if (opcode.OperandType != OperandType.InlineSwitch) + throw new ArgumentException ("opcode"); + + return new Instruction (opcode, targets); + } + + public static Instruction Create (OpCode opcode, VariableDefinition variable) + { + if (variable == null) + throw new ArgumentNullException ("variable"); + if (opcode.OperandType != OperandType.ShortInlineVar && + opcode.OperandType != OperandType.InlineVar) + throw new ArgumentException ("opcode"); + + return new Instruction (opcode, variable); + } + + public static Instruction Create (OpCode opcode, ParameterDefinition parameter) + { + if (parameter == null) + throw new ArgumentNullException ("parameter"); + if (opcode.OperandType != OperandType.ShortInlineArg && + opcode.OperandType != OperandType.InlineArg) + throw new ArgumentException ("opcode"); + + return new Instruction (opcode, parameter); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Instruction.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Instruction.cs.meta new file mode 100644 index 0000000..fc5ea61 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Instruction.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2d1536adbd2ca174abbd624473e722c3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/MethodBody.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/MethodBody.cs new file mode 100644 index 0000000..9a49f12 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/MethodBody.cs @@ -0,0 +1,426 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System; +using System.Threading; + +namespace MonoFN.Cecil.Cil { + + public sealed class MethodBody { + + readonly internal MethodDefinition method; + + internal ParameterDefinition this_parameter; + internal int max_stack_size; + internal int code_size; + internal bool init_locals; + internal MetadataToken local_var_token; + + internal Collection instructions; + internal Collection exceptions; + internal Collection variables; + + public MethodDefinition Method { + get { return method; } + } + + public int MaxStackSize { + get { return max_stack_size; } + set { max_stack_size = value; } + } + + public int CodeSize { + get { return code_size; } + } + + public bool InitLocals { + get { return init_locals; } + set { init_locals = value; } + } + + public MetadataToken LocalVarToken { + get { return local_var_token; } + set { local_var_token = value; } + } + + public Collection Instructions { + get { + if (instructions == null) + Interlocked.CompareExchange (ref instructions, new InstructionCollection (method), null); + + return instructions; + } + } + + public bool HasExceptionHandlers { + get { return !exceptions.IsNullOrEmpty (); } + } + + public Collection ExceptionHandlers { + get { + if (exceptions == null) + Interlocked.CompareExchange (ref exceptions, new Collection (), null); + + return exceptions; + } + } + + public bool HasVariables { + get { return !variables.IsNullOrEmpty (); } + } + + public Collection Variables { + get { + if (variables == null) + Interlocked.CompareExchange (ref variables, new VariableDefinitionCollection (this.method), null); + + return variables; + } + } + + public ParameterDefinition ThisParameter { + get { + if (method == null || method.DeclaringType == null) + throw new NotSupportedException (); + + if (!method.HasThis) + return null; + + if (this_parameter == null) + Interlocked.CompareExchange (ref this_parameter, CreateThisParameter (method), null); + + return this_parameter; + } + } + + static ParameterDefinition CreateThisParameter (MethodDefinition method) + { + var parameter_type = method.DeclaringType as TypeReference; + + if (parameter_type.HasGenericParameters) { + var instance = new GenericInstanceType (parameter_type, parameter_type.GenericParameters.Count); + for (int i = 0; i < parameter_type.GenericParameters.Count; i++) + instance.GenericArguments.Add (parameter_type.GenericParameters [i]); + + parameter_type = instance; + + } + + if (parameter_type.IsValueType || parameter_type.IsPrimitive) + parameter_type = new ByReferenceType (parameter_type); + + return new ParameterDefinition (parameter_type, method); + } + + public MethodBody (MethodDefinition method) + { + this.method = method; + } + + public ILProcessor GetILProcessor () + { + return new ILProcessor (this); + } + } + + sealed class VariableDefinitionCollection : Collection { + + readonly MethodDefinition method; + + internal VariableDefinitionCollection (MethodDefinition method) + { + this.method = method; + } + + internal VariableDefinitionCollection (MethodDefinition method, int capacity) + : base (capacity) + { + this.method = method; + } + + protected override void OnAdd (VariableDefinition item, int index) + { + item.index = index; + } + + protected override void OnInsert (VariableDefinition item, int index) + { + item.index = index; + UpdateVariableIndices (index, 1); + } + + protected override void OnSet (VariableDefinition item, int index) + { + item.index = index; + } + + protected override void OnRemove (VariableDefinition item, int index) + { + UpdateVariableIndices (index + 1, -1, item); + item.index = -1; + } + + void UpdateVariableIndices (int startIndex, int offset, VariableDefinition variableToRemove = null) + { + for (int i = startIndex; i < size; i++) + items [i].index = i + offset; + + var debug_info = method == null ? null : method.debug_info; + if (debug_info == null || debug_info.Scope == null) + return; + + foreach (var scope in debug_info.GetScopes ()) { + if (!scope.HasVariables) + continue; + + var variables = scope.Variables; + int variableDebugInfoIndexToRemove = -1; + for (int i = 0; i < variables.Count; i++) { + var variable = variables [i]; + + // If a variable is being removed detect if it has debug info counterpart, if so remove that as well. + // Note that the debug info can be either resolved (has direct reference to the VariableDefinition) + // or unresolved (has only the number index of the variable) - this needs to handle both cases. + if (variableToRemove != null && + ((variable.index.IsResolved && variable.index.ResolvedVariable == variableToRemove) || + (!variable.index.IsResolved && variable.Index == variableToRemove.Index))) { + variableDebugInfoIndexToRemove = i; + continue; + } + + // For unresolved debug info updates indeces to keep them pointing to the same variable. + if (!variable.index.IsResolved && variable.Index >= startIndex) { + variable.index = new VariableIndex (variable.Index + offset); + } + } + + if (variableDebugInfoIndexToRemove >= 0) + variables.RemoveAt (variableDebugInfoIndexToRemove); + } + } + } + + class InstructionCollection : Collection { + + readonly MethodDefinition method; + + internal InstructionCollection (MethodDefinition method) + { + this.method = method; + } + + internal InstructionCollection (MethodDefinition method, int capacity) + : base (capacity) + { + this.method = method; + } + + protected override void OnAdd (Instruction item, int index) + { + if (index == 0) + return; + + var previous = items [index - 1]; + previous.next = item; + item.previous = previous; + } + + protected override void OnInsert (Instruction item, int index) + { + int startOffset = 0; + if (size != 0) { + var current = items [index]; + if (current == null) { + var last = items [index - 1]; + last.next = item; + item.previous = last; + return; + } + + startOffset = current.Offset; + + var previous = current.previous; + if (previous != null) { + previous.next = item; + item.previous = previous; + } + + current.previous = item; + item.next = current; + } + + UpdateLocalScopes (null, null); + } + + protected override void OnSet (Instruction item, int index) + { + var current = items [index]; + + item.previous = current.previous; + item.next = current.next; + + current.previous = null; + current.next = null; + + UpdateLocalScopes (item, current); + } + + protected override void OnRemove (Instruction item, int index) + { + var previous = item.previous; + if (previous != null) + previous.next = item.next; + + var next = item.next; + if (next != null) + next.previous = item.previous; + + RemoveSequencePoint (item); + UpdateLocalScopes (item, next ?? previous); + + item.previous = null; + item.next = null; + } + + void RemoveSequencePoint (Instruction instruction) + { + var debug_info = method.debug_info; + if (debug_info == null || !debug_info.HasSequencePoints) + return; + + var sequence_points = debug_info.sequence_points; + for (int i = 0; i < sequence_points.Count; i++) { + if (sequence_points [i].Offset == instruction.offset) { + sequence_points.RemoveAt (i); + return; + } + } + } + + void UpdateLocalScopes (Instruction removedInstruction, Instruction existingInstruction) + { + var debug_info = method.debug_info; + if (debug_info == null) + return; + + // Local scopes store start/end pair of "instruction offsets". Instruction offset can be either resolved, in which case it + // has a reference to Instruction, or unresolved in which case it stores numerical offset (instruction offset in the body). + // Typically local scopes loaded from PE/PDB files will be resolved, but it's not a requirement. + // Each instruction has its own offset, which is populated on load, but never updated (this would be pretty expensive to do). + // Instructions created during the editting will typically have offset 0 (so incorrect). + // Local scopes created during editing will also likely be resolved (so no numerical offsets). + // So while local scopes which are unresolved are relatively rare if they appear, manipulating them based + // on the offsets allone is pretty hard (since we can't rely on correct offsets of instructions). + // On the other hand resolved local scopes are easy to maintain, since they point to instructions and thus inserting + // instructions is basically a no-op and removing instructions is as easy as changing the pointer. + // For this reason the algorithm here is: + // - First make sure that all instruction offsets are resolved - if not - resolve them + // - First time this will be relatively expensinve as it will walk the entire method body to convert offsets to instruction pointers + // Almost all local scopes are stored in the "right" order (sequentially per start offsets), so the code uses a simple one-item + // cache instruction<->offset to avoid walking instructions multiple times (that would only happen for scopes which are out of order). + // - Subsequent calls should be cheap as it will only walk all local scopes without doing anything + // - If there was an edit on local scope which makes some of them unresolved, the cost is proportional + // - Then update as necessary by manipulaitng instruction references alone + + InstructionOffsetCache cache = new InstructionOffsetCache () { + Offset = 0, + Index = 0, + Instruction = items [0] + }; + + UpdateLocalScope (debug_info.Scope, removedInstruction, existingInstruction, ref cache); + } + + void UpdateLocalScope (ScopeDebugInformation scope, Instruction removedInstruction, Instruction existingInstruction, ref InstructionOffsetCache cache) + { + if (scope == null) + return; + + if (!scope.Start.IsResolved) + scope.Start = ResolveInstructionOffset (scope.Start, ref cache); + + if (!scope.Start.IsEndOfMethod && scope.Start.ResolvedInstruction == removedInstruction) + scope.Start = new InstructionOffset (existingInstruction); + + if (scope.HasScopes) { + foreach (var subScope in scope.Scopes) + UpdateLocalScope (subScope, removedInstruction, existingInstruction, ref cache); + } + + if (!scope.End.IsResolved) + scope.End = ResolveInstructionOffset (scope.End, ref cache); + + if (!scope.End.IsEndOfMethod && scope.End.ResolvedInstruction == removedInstruction) + scope.End = new InstructionOffset (existingInstruction); + } + + struct InstructionOffsetCache { + public int Offset; + public int Index; + public Instruction Instruction; + } + + InstructionOffset ResolveInstructionOffset (InstructionOffset inputOffset, ref InstructionOffsetCache cache) + { + if (inputOffset.IsResolved) + return inputOffset; + + int offset = inputOffset.Offset; + + if (cache.Offset == offset) + return new InstructionOffset (cache.Instruction); + + if (cache.Offset > offset) { + // This should be rare - we're resolving offset pointing to a place before the current cache position + // resolve by walking the instructions from start and don't cache the result. + int size = 0; + for (int i = 0; i < items.Length; i++) { + if (size == offset) + return new InstructionOffset (items [i]); + + if (size > offset) + return new InstructionOffset (items [i - 1]); + + size += items [i].GetSize (); + } + + // Offset is larger than the size of the body - so it points after the end + return new InstructionOffset (); + } else { + // The offset points after the current cache position - so continue counting and update the cache + int size = cache.Offset; + for (int i = cache.Index; i < items.Length; i++) { + cache.Index = i; + cache.Offset = size; + + var item = items [i]; + + // Allow for trailing null values in the case of + // instructions.Size < instructions.Capacity + if (item == null) + break; + + cache.Instruction = item; + + if (cache.Offset == offset) + return new InstructionOffset (cache.Instruction); + + if (cache.Offset > offset) + return new InstructionOffset (items [i - 1]); + + size += item.GetSize (); + } + + return new InstructionOffset (); + } + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/MethodBody.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/MethodBody.cs.meta new file mode 100644 index 0000000..6bfd730 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/MethodBody.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4be4045de2c6a0e428e5910f4e76cfac +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/OpCode.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/OpCode.cs new file mode 100644 index 0000000..6db5b62 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/OpCode.cs @@ -0,0 +1,439 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil.Cil { + + public enum FlowControl { + Branch, + Break, + Call, + Cond_Branch, + Meta, + Next, + Phi, + Return, + Throw, + } + + public enum OpCodeType { + Annotation, + Macro, + Nternal, + Objmodel, + Prefix, + Primitive, + } + + public enum OperandType { + InlineBrTarget, + InlineField, + InlineI, + InlineI8, + InlineMethod, + InlineNone, + InlinePhi, + InlineR, + InlineSig, + InlineString, + InlineSwitch, + InlineTok, + InlineType, + InlineVar, + InlineArg, + ShortInlineBrTarget, + ShortInlineI, + ShortInlineR, + ShortInlineVar, + ShortInlineArg, + } + + public enum StackBehaviour { + Pop0, + Pop1, + Pop1_pop1, + Popi, + Popi_pop1, + Popi_popi, + Popi_popi8, + Popi_popi_popi, + Popi_popr4, + Popi_popr8, + Popref, + Popref_pop1, + Popref_popi, + Popref_popi_popi, + Popref_popi_popi8, + Popref_popi_popr4, + Popref_popi_popr8, + Popref_popi_popref, + PopAll, + Push0, + Push1, + Push1_push1, + Pushi, + Pushi8, + Pushr4, + Pushr8, + Pushref, + Varpop, + Varpush, + } + + public struct OpCode : IEquatable { + + readonly byte op1; + readonly byte op2; + readonly byte code; + readonly byte flow_control; + readonly byte opcode_type; + readonly byte operand_type; + readonly byte stack_behavior_pop; + readonly byte stack_behavior_push; + + public string Name { + get { return OpCodeNames.names [(int)Code]; } + } + + public int Size { + get { return op1 == 0xff ? 1 : 2; } + } + + public byte Op1 { + get { return op1; } + } + + public byte Op2 { + get { return op2; } + } + + public short Value { + get { return op1 == 0xff ? op2 : (short)((op1 << 8) | op2); } + } + + public Code Code { + get { return (Code)code; } + } + + public FlowControl FlowControl { + get { return (FlowControl)flow_control; } + } + + public OpCodeType OpCodeType { + get { return (OpCodeType)opcode_type; } + } + + public OperandType OperandType { + get { return (OperandType)operand_type; } + } + + public StackBehaviour StackBehaviourPop { + get { return (StackBehaviour)stack_behavior_pop; } + } + + public StackBehaviour StackBehaviourPush { + get { return (StackBehaviour)stack_behavior_push; } + } + + internal OpCode (int x, int y) + { + this.op1 = (byte)((x >> 0) & 0xff); + this.op2 = (byte)((x >> 8) & 0xff); + this.code = (byte)((x >> 16) & 0xff); + this.flow_control = (byte)((x >> 24) & 0xff); + + this.opcode_type = (byte)((y >> 0) & 0xff); + this.operand_type = (byte)((y >> 8) & 0xff); + this.stack_behavior_pop = (byte)((y >> 16) & 0xff); + this.stack_behavior_push = (byte)((y >> 24) & 0xff); + + if (op1 == 0xff) + OpCodes.OneByteOpCode [op2] = this; + else + OpCodes.TwoBytesOpCode [op2] = this; + } + + public override int GetHashCode () + { + return Value; + } + + public override bool Equals (object obj) + { + if (!(obj is OpCode)) + return false; + + var opcode = (OpCode)obj; + return op1 == opcode.op1 && op2 == opcode.op2; + } + + public bool Equals (OpCode opcode) + { + return op1 == opcode.op1 && op2 == opcode.op2; + } + + public static bool operator == (OpCode one, OpCode other) + { + return one.op1 == other.op1 && one.op2 == other.op2; + } + + public static bool operator != (OpCode one, OpCode other) + { + return one.op1 != other.op1 || one.op2 != other.op2; + } + + public override string ToString () + { + return Name; + } + } + + static class OpCodeNames { + + internal static readonly string [] names; + + static OpCodeNames () + { + var table = new byte [] { + 3, 110, 111, 112, + 5, 98, 114, 101, 97, 107, + 7, 108, 100, 97, 114, 103, 46, 48, + 7, 108, 100, 97, 114, 103, 46, 49, + 7, 108, 100, 97, 114, 103, 46, 50, + 7, 108, 100, 97, 114, 103, 46, 51, + 7, 108, 100, 108, 111, 99, 46, 48, + 7, 108, 100, 108, 111, 99, 46, 49, + 7, 108, 100, 108, 111, 99, 46, 50, + 7, 108, 100, 108, 111, 99, 46, 51, + 7, 115, 116, 108, 111, 99, 46, 48, + 7, 115, 116, 108, 111, 99, 46, 49, + 7, 115, 116, 108, 111, 99, 46, 50, + 7, 115, 116, 108, 111, 99, 46, 51, + 7, 108, 100, 97, 114, 103, 46, 115, + 8, 108, 100, 97, 114, 103, 97, 46, 115, + 7, 115, 116, 97, 114, 103, 46, 115, + 7, 108, 100, 108, 111, 99, 46, 115, + 8, 108, 100, 108, 111, 99, 97, 46, 115, + 7, 115, 116, 108, 111, 99, 46, 115, + 6, 108, 100, 110, 117, 108, 108, + 9, 108, 100, 99, 46, 105, 52, 46, 109, 49, + 8, 108, 100, 99, 46, 105, 52, 46, 48, + 8, 108, 100, 99, 46, 105, 52, 46, 49, + 8, 108, 100, 99, 46, 105, 52, 46, 50, + 8, 108, 100, 99, 46, 105, 52, 46, 51, + 8, 108, 100, 99, 46, 105, 52, 46, 52, + 8, 108, 100, 99, 46, 105, 52, 46, 53, + 8, 108, 100, 99, 46, 105, 52, 46, 54, + 8, 108, 100, 99, 46, 105, 52, 46, 55, + 8, 108, 100, 99, 46, 105, 52, 46, 56, + 8, 108, 100, 99, 46, 105, 52, 46, 115, + 6, 108, 100, 99, 46, 105, 52, + 6, 108, 100, 99, 46, 105, 56, + 6, 108, 100, 99, 46, 114, 52, + 6, 108, 100, 99, 46, 114, 56, + 3, 100, 117, 112, + 3, 112, 111, 112, + 3, 106, 109, 112, + 4, 99, 97, 108, 108, + 5, 99, 97, 108, 108, 105, + 3, 114, 101, 116, + 4, 98, 114, 46, 115, + 9, 98, 114, 102, 97, 108, 115, 101, 46, 115, + 8, 98, 114, 116, 114, 117, 101, 46, 115, + 5, 98, 101, 113, 46, 115, + 5, 98, 103, 101, 46, 115, + 5, 98, 103, 116, 46, 115, + 5, 98, 108, 101, 46, 115, + 5, 98, 108, 116, 46, 115, + 8, 98, 110, 101, 46, 117, 110, 46, 115, + 8, 98, 103, 101, 46, 117, 110, 46, 115, + 8, 98, 103, 116, 46, 117, 110, 46, 115, + 8, 98, 108, 101, 46, 117, 110, 46, 115, + 8, 98, 108, 116, 46, 117, 110, 46, 115, + 2, 98, 114, + 7, 98, 114, 102, 97, 108, 115, 101, + 6, 98, 114, 116, 114, 117, 101, + 3, 98, 101, 113, + 3, 98, 103, 101, + 3, 98, 103, 116, + 3, 98, 108, 101, + 3, 98, 108, 116, + 6, 98, 110, 101, 46, 117, 110, + 6, 98, 103, 101, 46, 117, 110, + 6, 98, 103, 116, 46, 117, 110, + 6, 98, 108, 101, 46, 117, 110, + 6, 98, 108, 116, 46, 117, 110, + 6, 115, 119, 105, 116, 99, 104, + 8, 108, 100, 105, 110, 100, 46, 105, 49, + 8, 108, 100, 105, 110, 100, 46, 117, 49, + 8, 108, 100, 105, 110, 100, 46, 105, 50, + 8, 108, 100, 105, 110, 100, 46, 117, 50, + 8, 108, 100, 105, 110, 100, 46, 105, 52, + 8, 108, 100, 105, 110, 100, 46, 117, 52, + 8, 108, 100, 105, 110, 100, 46, 105, 56, + 7, 108, 100, 105, 110, 100, 46, 105, + 8, 108, 100, 105, 110, 100, 46, 114, 52, + 8, 108, 100, 105, 110, 100, 46, 114, 56, + 9, 108, 100, 105, 110, 100, 46, 114, 101, 102, + 9, 115, 116, 105, 110, 100, 46, 114, 101, 102, + 8, 115, 116, 105, 110, 100, 46, 105, 49, + 8, 115, 116, 105, 110, 100, 46, 105, 50, + 8, 115, 116, 105, 110, 100, 46, 105, 52, + 8, 115, 116, 105, 110, 100, 46, 105, 56, + 8, 115, 116, 105, 110, 100, 46, 114, 52, + 8, 115, 116, 105, 110, 100, 46, 114, 56, + 3, 97, 100, 100, + 3, 115, 117, 98, + 3, 109, 117, 108, + 3, 100, 105, 118, + 6, 100, 105, 118, 46, 117, 110, + 3, 114, 101, 109, + 6, 114, 101, 109, 46, 117, 110, + 3, 97, 110, 100, + 2, 111, 114, + 3, 120, 111, 114, + 3, 115, 104, 108, + 3, 115, 104, 114, + 6, 115, 104, 114, 46, 117, 110, + 3, 110, 101, 103, + 3, 110, 111, 116, + 7, 99, 111, 110, 118, 46, 105, 49, + 7, 99, 111, 110, 118, 46, 105, 50, + 7, 99, 111, 110, 118, 46, 105, 52, + 7, 99, 111, 110, 118, 46, 105, 56, + 7, 99, 111, 110, 118, 46, 114, 52, + 7, 99, 111, 110, 118, 46, 114, 56, + 7, 99, 111, 110, 118, 46, 117, 52, + 7, 99, 111, 110, 118, 46, 117, 56, + 8, 99, 97, 108, 108, 118, 105, 114, 116, + 5, 99, 112, 111, 98, 106, + 5, 108, 100, 111, 98, 106, + 5, 108, 100, 115, 116, 114, + 6, 110, 101, 119, 111, 98, 106, + 9, 99, 97, 115, 116, 99, 108, 97, 115, 115, + 6, 105, 115, 105, 110, 115, 116, + 9, 99, 111, 110, 118, 46, 114, 46, 117, 110, + 5, 117, 110, 98, 111, 120, + 5, 116, 104, 114, 111, 119, + 5, 108, 100, 102, 108, 100, + 6, 108, 100, 102, 108, 100, 97, + 5, 115, 116, 102, 108, 100, + 6, 108, 100, 115, 102, 108, 100, + 7, 108, 100, 115, 102, 108, 100, 97, + 6, 115, 116, 115, 102, 108, 100, + 5, 115, 116, 111, 98, 106, + 14, 99, 111, 110, 118, 46, 111, 118, 102, 46, 105, 49, 46, 117, 110, + 14, 99, 111, 110, 118, 46, 111, 118, 102, 46, 105, 50, 46, 117, 110, + 14, 99, 111, 110, 118, 46, 111, 118, 102, 46, 105, 52, 46, 117, 110, + 14, 99, 111, 110, 118, 46, 111, 118, 102, 46, 105, 56, 46, 117, 110, + 14, 99, 111, 110, 118, 46, 111, 118, 102, 46, 117, 49, 46, 117, 110, + 14, 99, 111, 110, 118, 46, 111, 118, 102, 46, 117, 50, 46, 117, 110, + 14, 99, 111, 110, 118, 46, 111, 118, 102, 46, 117, 52, 46, 117, 110, + 14, 99, 111, 110, 118, 46, 111, 118, 102, 46, 117, 56, 46, 117, 110, + 13, 99, 111, 110, 118, 46, 111, 118, 102, 46, 105, 46, 117, 110, + 13, 99, 111, 110, 118, 46, 111, 118, 102, 46, 117, 46, 117, 110, + 3, 98, 111, 120, + 6, 110, 101, 119, 97, 114, 114, + 5, 108, 100, 108, 101, 110, + 7, 108, 100, 101, 108, 101, 109, 97, + 9, 108, 100, 101, 108, 101, 109, 46, 105, 49, + 9, 108, 100, 101, 108, 101, 109, 46, 117, 49, + 9, 108, 100, 101, 108, 101, 109, 46, 105, 50, + 9, 108, 100, 101, 108, 101, 109, 46, 117, 50, + 9, 108, 100, 101, 108, 101, 109, 46, 105, 52, + 9, 108, 100, 101, 108, 101, 109, 46, 117, 52, + 9, 108, 100, 101, 108, 101, 109, 46, 105, 56, + 8, 108, 100, 101, 108, 101, 109, 46, 105, + 9, 108, 100, 101, 108, 101, 109, 46, 114, 52, + 9, 108, 100, 101, 108, 101, 109, 46, 114, 56, + 10, 108, 100, 101, 108, 101, 109, 46, 114, 101, 102, + 8, 115, 116, 101, 108, 101, 109, 46, 105, + 9, 115, 116, 101, 108, 101, 109, 46, 105, 49, + 9, 115, 116, 101, 108, 101, 109, 46, 105, 50, + 9, 115, 116, 101, 108, 101, 109, 46, 105, 52, + 9, 115, 116, 101, 108, 101, 109, 46, 105, 56, + 9, 115, 116, 101, 108, 101, 109, 46, 114, 52, + 9, 115, 116, 101, 108, 101, 109, 46, 114, 56, + 10, 115, 116, 101, 108, 101, 109, 46, 114, 101, 102, + 10, 108, 100, 101, 108, 101, 109, 46, 97, 110, 121, + 10, 115, 116, 101, 108, 101, 109, 46, 97, 110, 121, + 9, 117, 110, 98, 111, 120, 46, 97, 110, 121, + 11, 99, 111, 110, 118, 46, 111, 118, 102, 46, 105, 49, + 11, 99, 111, 110, 118, 46, 111, 118, 102, 46, 117, 49, + 11, 99, 111, 110, 118, 46, 111, 118, 102, 46, 105, 50, + 11, 99, 111, 110, 118, 46, 111, 118, 102, 46, 117, 50, + 11, 99, 111, 110, 118, 46, 111, 118, 102, 46, 105, 52, + 11, 99, 111, 110, 118, 46, 111, 118, 102, 46, 117, 52, + 11, 99, 111, 110, 118, 46, 111, 118, 102, 46, 105, 56, + 11, 99, 111, 110, 118, 46, 111, 118, 102, 46, 117, 56, + 9, 114, 101, 102, 97, 110, 121, 118, 97, 108, + 8, 99, 107, 102, 105, 110, 105, 116, 101, + 8, 109, 107, 114, 101, 102, 97, 110, 121, + 7, 108, 100, 116, 111, 107, 101, 110, + 7, 99, 111, 110, 118, 46, 117, 50, + 7, 99, 111, 110, 118, 46, 117, 49, + 6, 99, 111, 110, 118, 46, 105, + 10, 99, 111, 110, 118, 46, 111, 118, 102, 46, 105, + 10, 99, 111, 110, 118, 46, 111, 118, 102, 46, 117, + 7, 97, 100, 100, 46, 111, 118, 102, + 10, 97, 100, 100, 46, 111, 118, 102, 46, 117, 110, + 7, 109, 117, 108, 46, 111, 118, 102, + 10, 109, 117, 108, 46, 111, 118, 102, 46, 117, 110, + 7, 115, 117, 98, 46, 111, 118, 102, + 10, 115, 117, 98, 46, 111, 118, 102, 46, 117, 110, + 10, 101, 110, 100, 102, 105, 110, 97, 108, 108, 121, + 5, 108, 101, 97, 118, 101, + 7, 108, 101, 97, 118, 101, 46, 115, + 7, 115, 116, 105, 110, 100, 46, 105, + 6, 99, 111, 110, 118, 46, 117, + 7, 97, 114, 103, 108, 105, 115, 116, + 3, 99, 101, 113, + 3, 99, 103, 116, + 6, 99, 103, 116, 46, 117, 110, + 3, 99, 108, 116, + 6, 99, 108, 116, 46, 117, 110, + 5, 108, 100, 102, 116, 110, + 9, 108, 100, 118, 105, 114, 116, 102, 116, 110, + 5, 108, 100, 97, 114, 103, + 6, 108, 100, 97, 114, 103, 97, + 5, 115, 116, 97, 114, 103, + 5, 108, 100, 108, 111, 99, + 6, 108, 100, 108, 111, 99, 97, + 5, 115, 116, 108, 111, 99, + 8, 108, 111, 99, 97, 108, 108, 111, 99, + 9, 101, 110, 100, 102, 105, 108, 116, 101, 114, + 10, 117, 110, 97, 108, 105, 103, 110, 101, 100, 46, + 9, 118, 111, 108, 97, 116, 105, 108, 101, 46, + 5, 116, 97, 105, 108, 46, + 7, 105, 110, 105, 116, 111, 98, 106, + 12, 99, 111, 110, 115, 116, 114, 97, 105, 110, 101, 100, 46, + 5, 99, 112, 98, 108, 107, + 7, 105, 110, 105, 116, 98, 108, 107, + 3, 110, 111, 46, + 7, 114, 101, 116, 104, 114, 111, 119, + 6, 115, 105, 122, 101, 111, 102, + 10, 114, 101, 102, 97, 110, 121, 116, 121, 112, 101, + 9, 114, 101, 97, 100, 111, 110, 108, 121, 46, + }; + + names = new string [219]; + + for (int i = 0, p = 0; i < names.Length; i++) { + var buffer = new char [table [p++]]; + + for (int j = 0; j < buffer.Length; j++) + buffer [j] = (char)table [p++]; + + names [i] = new string (buffer); + } + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/OpCode.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/OpCode.cs.meta new file mode 100644 index 0000000..21d9966 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/OpCode.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5d8b234c106529441912c10b87502175 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/OpCodes.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/OpCodes.cs new file mode 100644 index 0000000..f05d164 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/OpCodes.cs @@ -0,0 +1,894 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil.Cil { + + public static class OpCodes { + + internal static readonly OpCode [] OneByteOpCode = new OpCode [0xe0 + 1]; + internal static readonly OpCode [] TwoBytesOpCode = new OpCode [0x1e + 1]; + + public static readonly OpCode Nop = new OpCode ( + 0xff << 0 | 0x00 << 8 | (byte)Code.Nop << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Break = new OpCode ( + 0xff << 0 | 0x01 << 8 | (byte)Code.Break << 16 | (byte)FlowControl.Break << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Ldarg_0 = new OpCode ( + 0xff << 0 | 0x02 << 8 | (byte)Code.Ldarg_0 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Ldarg_1 = new OpCode ( + 0xff << 0 | 0x03 << 8 | (byte)Code.Ldarg_1 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Ldarg_2 = new OpCode ( + 0xff << 0 | 0x04 << 8 | (byte)Code.Ldarg_2 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Ldarg_3 = new OpCode ( + 0xff << 0 | 0x05 << 8 | (byte)Code.Ldarg_3 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Ldloc_0 = new OpCode ( + 0xff << 0 | 0x06 << 8 | (byte)Code.Ldloc_0 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Ldloc_1 = new OpCode ( + 0xff << 0 | 0x07 << 8 | (byte)Code.Ldloc_1 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Ldloc_2 = new OpCode ( + 0xff << 0 | 0x08 << 8 | (byte)Code.Ldloc_2 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Ldloc_3 = new OpCode ( + 0xff << 0 | 0x09 << 8 | (byte)Code.Ldloc_3 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Stloc_0 = new OpCode ( + 0xff << 0 | 0x0a << 8 | (byte)Code.Stloc_0 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Stloc_1 = new OpCode ( + 0xff << 0 | 0x0b << 8 | (byte)Code.Stloc_1 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Stloc_2 = new OpCode ( + 0xff << 0 | 0x0c << 8 | (byte)Code.Stloc_2 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Stloc_3 = new OpCode ( + 0xff << 0 | 0x0d << 8 | (byte)Code.Stloc_3 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Ldarg_S = new OpCode ( + 0xff << 0 | 0x0e << 8 | (byte)Code.Ldarg_S << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.ShortInlineArg << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Ldarga_S = new OpCode ( + 0xff << 0 | 0x0f << 8 | (byte)Code.Ldarga_S << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.ShortInlineArg << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Starg_S = new OpCode ( + 0xff << 0 | 0x10 << 8 | (byte)Code.Starg_S << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.ShortInlineArg << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Ldloc_S = new OpCode ( + 0xff << 0 | 0x11 << 8 | (byte)Code.Ldloc_S << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.ShortInlineVar << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Ldloca_S = new OpCode ( + 0xff << 0 | 0x12 << 8 | (byte)Code.Ldloca_S << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.ShortInlineVar << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Stloc_S = new OpCode ( + 0xff << 0 | 0x13 << 8 | (byte)Code.Stloc_S << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.ShortInlineVar << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Ldnull = new OpCode ( + 0xff << 0 | 0x14 << 8 | (byte)Code.Ldnull << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushref << 24); + + public static readonly OpCode Ldc_I4_M1 = new OpCode ( + 0xff << 0 | 0x15 << 8 | (byte)Code.Ldc_I4_M1 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldc_I4_0 = new OpCode ( + 0xff << 0 | 0x16 << 8 | (byte)Code.Ldc_I4_0 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldc_I4_1 = new OpCode ( + 0xff << 0 | 0x17 << 8 | (byte)Code.Ldc_I4_1 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldc_I4_2 = new OpCode ( + 0xff << 0 | 0x18 << 8 | (byte)Code.Ldc_I4_2 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldc_I4_3 = new OpCode ( + 0xff << 0 | 0x19 << 8 | (byte)Code.Ldc_I4_3 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldc_I4_4 = new OpCode ( + 0xff << 0 | 0x1a << 8 | (byte)Code.Ldc_I4_4 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldc_I4_5 = new OpCode ( + 0xff << 0 | 0x1b << 8 | (byte)Code.Ldc_I4_5 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldc_I4_6 = new OpCode ( + 0xff << 0 | 0x1c << 8 | (byte)Code.Ldc_I4_6 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldc_I4_7 = new OpCode ( + 0xff << 0 | 0x1d << 8 | (byte)Code.Ldc_I4_7 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldc_I4_8 = new OpCode ( + 0xff << 0 | 0x1e << 8 | (byte)Code.Ldc_I4_8 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldc_I4_S = new OpCode ( + 0xff << 0 | 0x1f << 8 | (byte)Code.Ldc_I4_S << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.ShortInlineI << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldc_I4 = new OpCode ( + 0xff << 0 | 0x20 << 8 | (byte)Code.Ldc_I4 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineI << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldc_I8 = new OpCode ( + 0xff << 0 | 0x21 << 8 | (byte)Code.Ldc_I8 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineI8 << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushi8 << 24); + + public static readonly OpCode Ldc_R4 = new OpCode ( + 0xff << 0 | 0x22 << 8 | (byte)Code.Ldc_R4 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.ShortInlineR << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushr4 << 24); + + public static readonly OpCode Ldc_R8 = new OpCode ( + 0xff << 0 | 0x23 << 8 | (byte)Code.Ldc_R8 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineR << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushr8 << 24); + + public static readonly OpCode Dup = new OpCode ( + 0xff << 0 | 0x25 << 8 | (byte)Code.Dup << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Push1_push1 << 24); + + public static readonly OpCode Pop = new OpCode ( + 0xff << 0 | 0x26 << 8 | (byte)Code.Pop << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Jmp = new OpCode ( + 0xff << 0 | 0x27 << 8 | (byte)Code.Jmp << 16 | (byte)FlowControl.Call << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineMethod << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Call = new OpCode ( + 0xff << 0 | 0x28 << 8 | (byte)Code.Call << 16 | (byte)FlowControl.Call << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineMethod << 8 | (byte)StackBehaviour.Varpop << 16 | (byte)StackBehaviour.Varpush << 24); + + public static readonly OpCode Calli = new OpCode ( + 0xff << 0 | 0x29 << 8 | (byte)Code.Calli << 16 | (byte)FlowControl.Call << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineSig << 8 | (byte)StackBehaviour.Varpop << 16 | (byte)StackBehaviour.Varpush << 24); + + public static readonly OpCode Ret = new OpCode ( + 0xff << 0 | 0x2a << 8 | (byte)Code.Ret << 16 | (byte)FlowControl.Return << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Varpop << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Br_S = new OpCode ( + 0xff << 0 | 0x2b << 8 | (byte)Code.Br_S << 16 | (byte)FlowControl.Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.ShortInlineBrTarget << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Brfalse_S = new OpCode ( + 0xff << 0 | 0x2c << 8 | (byte)Code.Brfalse_S << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.ShortInlineBrTarget << 8 | (byte)StackBehaviour.Popi << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Brtrue_S = new OpCode ( + 0xff << 0 | 0x2d << 8 | (byte)Code.Brtrue_S << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.ShortInlineBrTarget << 8 | (byte)StackBehaviour.Popi << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Beq_S = new OpCode ( + 0xff << 0 | 0x2e << 8 | (byte)Code.Beq_S << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.ShortInlineBrTarget << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Bge_S = new OpCode ( + 0xff << 0 | 0x2f << 8 | (byte)Code.Bge_S << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.ShortInlineBrTarget << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Bgt_S = new OpCode ( + 0xff << 0 | 0x30 << 8 | (byte)Code.Bgt_S << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.ShortInlineBrTarget << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Ble_S = new OpCode ( + 0xff << 0 | 0x31 << 8 | (byte)Code.Ble_S << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.ShortInlineBrTarget << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Blt_S = new OpCode ( + 0xff << 0 | 0x32 << 8 | (byte)Code.Blt_S << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.ShortInlineBrTarget << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Bne_Un_S = new OpCode ( + 0xff << 0 | 0x33 << 8 | (byte)Code.Bne_Un_S << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.ShortInlineBrTarget << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Bge_Un_S = new OpCode ( + 0xff << 0 | 0x34 << 8 | (byte)Code.Bge_Un_S << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.ShortInlineBrTarget << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Bgt_Un_S = new OpCode ( + 0xff << 0 | 0x35 << 8 | (byte)Code.Bgt_Un_S << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.ShortInlineBrTarget << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Ble_Un_S = new OpCode ( + 0xff << 0 | 0x36 << 8 | (byte)Code.Ble_Un_S << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.ShortInlineBrTarget << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Blt_Un_S = new OpCode ( + 0xff << 0 | 0x37 << 8 | (byte)Code.Blt_Un_S << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.ShortInlineBrTarget << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Br = new OpCode ( + 0xff << 0 | 0x38 << 8 | (byte)Code.Br << 16 | (byte)FlowControl.Branch << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineBrTarget << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Brfalse = new OpCode ( + 0xff << 0 | 0x39 << 8 | (byte)Code.Brfalse << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineBrTarget << 8 | (byte)StackBehaviour.Popi << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Brtrue = new OpCode ( + 0xff << 0 | 0x3a << 8 | (byte)Code.Brtrue << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineBrTarget << 8 | (byte)StackBehaviour.Popi << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Beq = new OpCode ( + 0xff << 0 | 0x3b << 8 | (byte)Code.Beq << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineBrTarget << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Bge = new OpCode ( + 0xff << 0 | 0x3c << 8 | (byte)Code.Bge << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineBrTarget << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Bgt = new OpCode ( + 0xff << 0 | 0x3d << 8 | (byte)Code.Bgt << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineBrTarget << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Ble = new OpCode ( + 0xff << 0 | 0x3e << 8 | (byte)Code.Ble << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineBrTarget << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Blt = new OpCode ( + 0xff << 0 | 0x3f << 8 | (byte)Code.Blt << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineBrTarget << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Bne_Un = new OpCode ( + 0xff << 0 | 0x40 << 8 | (byte)Code.Bne_Un << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineBrTarget << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Bge_Un = new OpCode ( + 0xff << 0 | 0x41 << 8 | (byte)Code.Bge_Un << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineBrTarget << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Bgt_Un = new OpCode ( + 0xff << 0 | 0x42 << 8 | (byte)Code.Bgt_Un << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineBrTarget << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Ble_Un = new OpCode ( + 0xff << 0 | 0x43 << 8 | (byte)Code.Ble_Un << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineBrTarget << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Blt_Un = new OpCode ( + 0xff << 0 | 0x44 << 8 | (byte)Code.Blt_Un << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.InlineBrTarget << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Switch = new OpCode ( + 0xff << 0 | 0x45 << 8 | (byte)Code.Switch << 16 | (byte)FlowControl.Cond_Branch << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineSwitch << 8 | (byte)StackBehaviour.Popi << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Ldind_I1 = new OpCode ( + 0xff << 0 | 0x46 << 8 | (byte)Code.Ldind_I1 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldind_U1 = new OpCode ( + 0xff << 0 | 0x47 << 8 | (byte)Code.Ldind_U1 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldind_I2 = new OpCode ( + 0xff << 0 | 0x48 << 8 | (byte)Code.Ldind_I2 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldind_U2 = new OpCode ( + 0xff << 0 | 0x49 << 8 | (byte)Code.Ldind_U2 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldind_I4 = new OpCode ( + 0xff << 0 | 0x4a << 8 | (byte)Code.Ldind_I4 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldind_U4 = new OpCode ( + 0xff << 0 | 0x4b << 8 | (byte)Code.Ldind_U4 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldind_I8 = new OpCode ( + 0xff << 0 | 0x4c << 8 | (byte)Code.Ldind_I8 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi << 16 | (byte)StackBehaviour.Pushi8 << 24); + + public static readonly OpCode Ldind_I = new OpCode ( + 0xff << 0 | 0x4d << 8 | (byte)Code.Ldind_I << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldind_R4 = new OpCode ( + 0xff << 0 | 0x4e << 8 | (byte)Code.Ldind_R4 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi << 16 | (byte)StackBehaviour.Pushr4 << 24); + + public static readonly OpCode Ldind_R8 = new OpCode ( + 0xff << 0 | 0x4f << 8 | (byte)Code.Ldind_R8 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi << 16 | (byte)StackBehaviour.Pushr8 << 24); + + public static readonly OpCode Ldind_Ref = new OpCode ( + 0xff << 0 | 0x50 << 8 | (byte)Code.Ldind_Ref << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi << 16 | (byte)StackBehaviour.Pushref << 24); + + public static readonly OpCode Stind_Ref = new OpCode ( + 0xff << 0 | 0x51 << 8 | (byte)Code.Stind_Ref << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi_popi << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Stind_I1 = new OpCode ( + 0xff << 0 | 0x52 << 8 | (byte)Code.Stind_I1 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi_popi << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Stind_I2 = new OpCode ( + 0xff << 0 | 0x53 << 8 | (byte)Code.Stind_I2 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi_popi << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Stind_I4 = new OpCode ( + 0xff << 0 | 0x54 << 8 | (byte)Code.Stind_I4 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi_popi << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Stind_I8 = new OpCode ( + 0xff << 0 | 0x55 << 8 | (byte)Code.Stind_I8 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi_popi8 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Stind_R4 = new OpCode ( + 0xff << 0 | 0x56 << 8 | (byte)Code.Stind_R4 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi_popr4 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Stind_R8 = new OpCode ( + 0xff << 0 | 0x57 << 8 | (byte)Code.Stind_R8 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi_popr8 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Add = new OpCode ( + 0xff << 0 | 0x58 << 8 | (byte)Code.Add << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Sub = new OpCode ( + 0xff << 0 | 0x59 << 8 | (byte)Code.Sub << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Mul = new OpCode ( + 0xff << 0 | 0x5a << 8 | (byte)Code.Mul << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Div = new OpCode ( + 0xff << 0 | 0x5b << 8 | (byte)Code.Div << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Div_Un = new OpCode ( + 0xff << 0 | 0x5c << 8 | (byte)Code.Div_Un << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Rem = new OpCode ( + 0xff << 0 | 0x5d << 8 | (byte)Code.Rem << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Rem_Un = new OpCode ( + 0xff << 0 | 0x5e << 8 | (byte)Code.Rem_Un << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode And = new OpCode ( + 0xff << 0 | 0x5f << 8 | (byte)Code.And << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Or = new OpCode ( + 0xff << 0 | 0x60 << 8 | (byte)Code.Or << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Xor = new OpCode ( + 0xff << 0 | 0x61 << 8 | (byte)Code.Xor << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Shl = new OpCode ( + 0xff << 0 | 0x62 << 8 | (byte)Code.Shl << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Shr = new OpCode ( + 0xff << 0 | 0x63 << 8 | (byte)Code.Shr << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Shr_Un = new OpCode ( + 0xff << 0 | 0x64 << 8 | (byte)Code.Shr_Un << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Neg = new OpCode ( + 0xff << 0 | 0x65 << 8 | (byte)Code.Neg << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Not = new OpCode ( + 0xff << 0 | 0x66 << 8 | (byte)Code.Not << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Conv_I1 = new OpCode ( + 0xff << 0 | 0x67 << 8 | (byte)Code.Conv_I1 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_I2 = new OpCode ( + 0xff << 0 | 0x68 << 8 | (byte)Code.Conv_I2 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_I4 = new OpCode ( + 0xff << 0 | 0x69 << 8 | (byte)Code.Conv_I4 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_I8 = new OpCode ( + 0xff << 0 | 0x6a << 8 | (byte)Code.Conv_I8 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi8 << 24); + + public static readonly OpCode Conv_R4 = new OpCode ( + 0xff << 0 | 0x6b << 8 | (byte)Code.Conv_R4 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushr4 << 24); + + public static readonly OpCode Conv_R8 = new OpCode ( + 0xff << 0 | 0x6c << 8 | (byte)Code.Conv_R8 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushr8 << 24); + + public static readonly OpCode Conv_U4 = new OpCode ( + 0xff << 0 | 0x6d << 8 | (byte)Code.Conv_U4 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_U8 = new OpCode ( + 0xff << 0 | 0x6e << 8 | (byte)Code.Conv_U8 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi8 << 24); + + public static readonly OpCode Callvirt = new OpCode ( + 0xff << 0 | 0x6f << 8 | (byte)Code.Callvirt << 16 | (byte)FlowControl.Call << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineMethod << 8 | (byte)StackBehaviour.Varpop << 16 | (byte)StackBehaviour.Varpush << 24); + + public static readonly OpCode Cpobj = new OpCode ( + 0xff << 0 | 0x70 << 8 | (byte)Code.Cpobj << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineType << 8 | (byte)StackBehaviour.Popi_popi << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Ldobj = new OpCode ( + 0xff << 0 | 0x71 << 8 | (byte)Code.Ldobj << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineType << 8 | (byte)StackBehaviour.Popi << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Ldstr = new OpCode ( + 0xff << 0 | 0x72 << 8 | (byte)Code.Ldstr << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineString << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushref << 24); + + public static readonly OpCode Newobj = new OpCode ( + 0xff << 0 | 0x73 << 8 | (byte)Code.Newobj << 16 | (byte)FlowControl.Call << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineMethod << 8 | (byte)StackBehaviour.Varpop << 16 | (byte)StackBehaviour.Pushref << 24); + + public static readonly OpCode Castclass = new OpCode ( + 0xff << 0 | 0x74 << 8 | (byte)Code.Castclass << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineType << 8 | (byte)StackBehaviour.Popref << 16 | (byte)StackBehaviour.Pushref << 24); + + public static readonly OpCode Isinst = new OpCode ( + 0xff << 0 | 0x75 << 8 | (byte)Code.Isinst << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineType << 8 | (byte)StackBehaviour.Popref << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_R_Un = new OpCode ( + 0xff << 0 | 0x76 << 8 | (byte)Code.Conv_R_Un << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushr8 << 24); + + public static readonly OpCode Unbox = new OpCode ( + 0xff << 0 | 0x79 << 8 | (byte)Code.Unbox << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineType << 8 | (byte)StackBehaviour.Popref << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Throw = new OpCode ( + 0xff << 0 | 0x7a << 8 | (byte)Code.Throw << 16 | (byte)FlowControl.Throw << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popref << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Ldfld = new OpCode ( + 0xff << 0 | 0x7b << 8 | (byte)Code.Ldfld << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineField << 8 | (byte)StackBehaviour.Popref << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Ldflda = new OpCode ( + 0xff << 0 | 0x7c << 8 | (byte)Code.Ldflda << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineField << 8 | (byte)StackBehaviour.Popref << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Stfld = new OpCode ( + 0xff << 0 | 0x7d << 8 | (byte)Code.Stfld << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineField << 8 | (byte)StackBehaviour.Popref_pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Ldsfld = new OpCode ( + 0xff << 0 | 0x7e << 8 | (byte)Code.Ldsfld << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineField << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Ldsflda = new OpCode ( + 0xff << 0 | 0x7f << 8 | (byte)Code.Ldsflda << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineField << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Stsfld = new OpCode ( + 0xff << 0 | 0x80 << 8 | (byte)Code.Stsfld << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineField << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Stobj = new OpCode ( + 0xff << 0 | 0x81 << 8 | (byte)Code.Stobj << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineType << 8 | (byte)StackBehaviour.Popi_pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Conv_Ovf_I1_Un = new OpCode ( + 0xff << 0 | 0x82 << 8 | (byte)Code.Conv_Ovf_I1_Un << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_Ovf_I2_Un = new OpCode ( + 0xff << 0 | 0x83 << 8 | (byte)Code.Conv_Ovf_I2_Un << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_Ovf_I4_Un = new OpCode ( + 0xff << 0 | 0x84 << 8 | (byte)Code.Conv_Ovf_I4_Un << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_Ovf_I8_Un = new OpCode ( + 0xff << 0 | 0x85 << 8 | (byte)Code.Conv_Ovf_I8_Un << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi8 << 24); + + public static readonly OpCode Conv_Ovf_U1_Un = new OpCode ( + 0xff << 0 | 0x86 << 8 | (byte)Code.Conv_Ovf_U1_Un << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_Ovf_U2_Un = new OpCode ( + 0xff << 0 | 0x87 << 8 | (byte)Code.Conv_Ovf_U2_Un << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_Ovf_U4_Un = new OpCode ( + 0xff << 0 | 0x88 << 8 | (byte)Code.Conv_Ovf_U4_Un << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_Ovf_U8_Un = new OpCode ( + 0xff << 0 | 0x89 << 8 | (byte)Code.Conv_Ovf_U8_Un << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi8 << 24); + + public static readonly OpCode Conv_Ovf_I_Un = new OpCode ( + 0xff << 0 | 0x8a << 8 | (byte)Code.Conv_Ovf_I_Un << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_Ovf_U_Un = new OpCode ( + 0xff << 0 | 0x8b << 8 | (byte)Code.Conv_Ovf_U_Un << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Box = new OpCode ( + 0xff << 0 | 0x8c << 8 | (byte)Code.Box << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineType << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushref << 24); + + public static readonly OpCode Newarr = new OpCode ( + 0xff << 0 | 0x8d << 8 | (byte)Code.Newarr << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineType << 8 | (byte)StackBehaviour.Popi << 16 | (byte)StackBehaviour.Pushref << 24); + + public static readonly OpCode Ldlen = new OpCode ( + 0xff << 0 | 0x8e << 8 | (byte)Code.Ldlen << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popref << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldelema = new OpCode ( + 0xff << 0 | 0x8f << 8 | (byte)Code.Ldelema << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineType << 8 | (byte)StackBehaviour.Popref_popi << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldelem_I1 = new OpCode ( + 0xff << 0 | 0x90 << 8 | (byte)Code.Ldelem_I1 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popref_popi << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldelem_U1 = new OpCode ( + 0xff << 0 | 0x91 << 8 | (byte)Code.Ldelem_U1 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popref_popi << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldelem_I2 = new OpCode ( + 0xff << 0 | 0x92 << 8 | (byte)Code.Ldelem_I2 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popref_popi << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldelem_U2 = new OpCode ( + 0xff << 0 | 0x93 << 8 | (byte)Code.Ldelem_U2 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popref_popi << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldelem_I4 = new OpCode ( + 0xff << 0 | 0x94 << 8 | (byte)Code.Ldelem_I4 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popref_popi << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldelem_U4 = new OpCode ( + 0xff << 0 | 0x95 << 8 | (byte)Code.Ldelem_U4 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popref_popi << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldelem_I8 = new OpCode ( + 0xff << 0 | 0x96 << 8 | (byte)Code.Ldelem_I8 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popref_popi << 16 | (byte)StackBehaviour.Pushi8 << 24); + + public static readonly OpCode Ldelem_I = new OpCode ( + 0xff << 0 | 0x97 << 8 | (byte)Code.Ldelem_I << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popref_popi << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldelem_R4 = new OpCode ( + 0xff << 0 | 0x98 << 8 | (byte)Code.Ldelem_R4 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popref_popi << 16 | (byte)StackBehaviour.Pushr4 << 24); + + public static readonly OpCode Ldelem_R8 = new OpCode ( + 0xff << 0 | 0x99 << 8 | (byte)Code.Ldelem_R8 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popref_popi << 16 | (byte)StackBehaviour.Pushr8 << 24); + + public static readonly OpCode Ldelem_Ref = new OpCode ( + 0xff << 0 | 0x9a << 8 | (byte)Code.Ldelem_Ref << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popref_popi << 16 | (byte)StackBehaviour.Pushref << 24); + + public static readonly OpCode Stelem_I = new OpCode ( + 0xff << 0 | 0x9b << 8 | (byte)Code.Stelem_I << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popref_popi_popi << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Stelem_I1 = new OpCode ( + 0xff << 0 | 0x9c << 8 | (byte)Code.Stelem_I1 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popref_popi_popi << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Stelem_I2 = new OpCode ( + 0xff << 0 | 0x9d << 8 | (byte)Code.Stelem_I2 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popref_popi_popi << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Stelem_I4 = new OpCode ( + 0xff << 0 | 0x9e << 8 | (byte)Code.Stelem_I4 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popref_popi_popi << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Stelem_I8 = new OpCode ( + 0xff << 0 | 0x9f << 8 | (byte)Code.Stelem_I8 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popref_popi_popi8 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Stelem_R4 = new OpCode ( + 0xff << 0 | 0xa0 << 8 | (byte)Code.Stelem_R4 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popref_popi_popr4 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Stelem_R8 = new OpCode ( + 0xff << 0 | 0xa1 << 8 | (byte)Code.Stelem_R8 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popref_popi_popr8 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Stelem_Ref = new OpCode ( + 0xff << 0 | 0xa2 << 8 | (byte)Code.Stelem_Ref << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popref_popi_popref << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Ldelem_Any = new OpCode ( + 0xff << 0 | 0xa3 << 8 | (byte)Code.Ldelem_Any << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineType << 8 | (byte)StackBehaviour.Popref_popi << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Stelem_Any = new OpCode ( + 0xff << 0 | 0xa4 << 8 | (byte)Code.Stelem_Any << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineType << 8 | (byte)StackBehaviour.Popref_popi_popref << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Unbox_Any = new OpCode ( + 0xff << 0 | 0xa5 << 8 | (byte)Code.Unbox_Any << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineType << 8 | (byte)StackBehaviour.Popref << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Conv_Ovf_I1 = new OpCode ( + 0xff << 0 | 0xb3 << 8 | (byte)Code.Conv_Ovf_I1 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_Ovf_U1 = new OpCode ( + 0xff << 0 | 0xb4 << 8 | (byte)Code.Conv_Ovf_U1 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_Ovf_I2 = new OpCode ( + 0xff << 0 | 0xb5 << 8 | (byte)Code.Conv_Ovf_I2 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_Ovf_U2 = new OpCode ( + 0xff << 0 | 0xb6 << 8 | (byte)Code.Conv_Ovf_U2 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_Ovf_I4 = new OpCode ( + 0xff << 0 | 0xb7 << 8 | (byte)Code.Conv_Ovf_I4 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_Ovf_U4 = new OpCode ( + 0xff << 0 | 0xb8 << 8 | (byte)Code.Conv_Ovf_U4 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_Ovf_I8 = new OpCode ( + 0xff << 0 | 0xb9 << 8 | (byte)Code.Conv_Ovf_I8 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi8 << 24); + + public static readonly OpCode Conv_Ovf_U8 = new OpCode ( + 0xff << 0 | 0xba << 8 | (byte)Code.Conv_Ovf_U8 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi8 << 24); + + public static readonly OpCode Refanyval = new OpCode ( + 0xff << 0 | 0xc2 << 8 | (byte)Code.Refanyval << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineType << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ckfinite = new OpCode ( + 0xff << 0 | 0xc3 << 8 | (byte)Code.Ckfinite << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushr8 << 24); + + public static readonly OpCode Mkrefany = new OpCode ( + 0xff << 0 | 0xc6 << 8 | (byte)Code.Mkrefany << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineType << 8 | (byte)StackBehaviour.Popi << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Ldtoken = new OpCode ( + 0xff << 0 | 0xd0 << 8 | (byte)Code.Ldtoken << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineTok << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_U2 = new OpCode ( + 0xff << 0 | 0xd1 << 8 | (byte)Code.Conv_U2 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_U1 = new OpCode ( + 0xff << 0 | 0xd2 << 8 | (byte)Code.Conv_U1 << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_I = new OpCode ( + 0xff << 0 | 0xd3 << 8 | (byte)Code.Conv_I << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_Ovf_I = new OpCode ( + 0xff << 0 | 0xd4 << 8 | (byte)Code.Conv_Ovf_I << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Conv_Ovf_U = new OpCode ( + 0xff << 0 | 0xd5 << 8 | (byte)Code.Conv_Ovf_U << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Add_Ovf = new OpCode ( + 0xff << 0 | 0xd6 << 8 | (byte)Code.Add_Ovf << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Add_Ovf_Un = new OpCode ( + 0xff << 0 | 0xd7 << 8 | (byte)Code.Add_Ovf_Un << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Mul_Ovf = new OpCode ( + 0xff << 0 | 0xd8 << 8 | (byte)Code.Mul_Ovf << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Mul_Ovf_Un = new OpCode ( + 0xff << 0 | 0xd9 << 8 | (byte)Code.Mul_Ovf_Un << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Sub_Ovf = new OpCode ( + 0xff << 0 | 0xda << 8 | (byte)Code.Sub_Ovf << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Sub_Ovf_Un = new OpCode ( + 0xff << 0 | 0xdb << 8 | (byte)Code.Sub_Ovf_Un << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Endfinally = new OpCode ( + 0xff << 0 | 0xdc << 8 | (byte)Code.Endfinally << 16 | (byte)FlowControl.Return << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Leave = new OpCode ( + 0xff << 0 | 0xdd << 8 | (byte)Code.Leave << 16 | (byte)FlowControl.Branch << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineBrTarget << 8 | (byte)StackBehaviour.PopAll << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Leave_S = new OpCode ( + 0xff << 0 | 0xde << 8 | (byte)Code.Leave_S << 16 | (byte)FlowControl.Branch << 24, + (byte)OpCodeType.Macro << 0 | (byte)OperandType.ShortInlineBrTarget << 8 | (byte)StackBehaviour.PopAll << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Stind_I = new OpCode ( + 0xff << 0 | 0xdf << 8 | (byte)Code.Stind_I << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi_popi << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Conv_U = new OpCode ( + 0xff << 0 | 0xe0 << 8 | (byte)Code.Conv_U << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Arglist = new OpCode ( + 0xfe << 0 | 0x00 << 8 | (byte)Code.Arglist << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ceq = new OpCode ( + 0xfe << 0 | 0x01 << 8 | (byte)Code.Ceq << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Cgt = new OpCode ( + 0xfe << 0 | 0x02 << 8 | (byte)Code.Cgt << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Cgt_Un = new OpCode ( + 0xfe << 0 | 0x03 << 8 | (byte)Code.Cgt_Un << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Clt = new OpCode ( + 0xfe << 0 | 0x04 << 8 | (byte)Code.Clt << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Clt_Un = new OpCode ( + 0xfe << 0 | 0x05 << 8 | (byte)Code.Clt_Un << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1_pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldftn = new OpCode ( + 0xfe << 0 | 0x06 << 8 | (byte)Code.Ldftn << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineMethod << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldvirtftn = new OpCode ( + 0xfe << 0 | 0x07 << 8 | (byte)Code.Ldvirtftn << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineMethod << 8 | (byte)StackBehaviour.Popref << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Ldarg = new OpCode ( + 0xfe << 0 | 0x09 << 8 | (byte)Code.Ldarg << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineArg << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Ldarga = new OpCode ( + 0xfe << 0 | 0x0a << 8 | (byte)Code.Ldarga << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineArg << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Starg = new OpCode ( + 0xfe << 0 | 0x0b << 8 | (byte)Code.Starg << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineArg << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Ldloc = new OpCode ( + 0xfe << 0 | 0x0c << 8 | (byte)Code.Ldloc << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineVar << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push1 << 24); + + public static readonly OpCode Ldloca = new OpCode ( + 0xfe << 0 | 0x0d << 8 | (byte)Code.Ldloca << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineVar << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Stloc = new OpCode ( + 0xfe << 0 | 0x0e << 8 | (byte)Code.Stloc << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineVar << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Localloc = new OpCode ( + 0xfe << 0 | 0x0f << 8 | (byte)Code.Localloc << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Endfilter = new OpCode ( + 0xfe << 0 | 0x11 << 8 | (byte)Code.Endfilter << 16 | (byte)FlowControl.Return << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Unaligned = new OpCode ( + 0xfe << 0 | 0x12 << 8 | (byte)Code.Unaligned << 16 | (byte)FlowControl.Meta << 24, + (byte)OpCodeType.Prefix << 0 | (byte)OperandType.ShortInlineI << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Volatile = new OpCode ( + 0xfe << 0 | 0x13 << 8 | (byte)Code.Volatile << 16 | (byte)FlowControl.Meta << 24, + (byte)OpCodeType.Prefix << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Tail = new OpCode ( + 0xfe << 0 | 0x14 << 8 | (byte)Code.Tail << 16 | (byte)FlowControl.Meta << 24, + (byte)OpCodeType.Prefix << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Initobj = new OpCode ( + 0xfe << 0 | 0x15 << 8 | (byte)Code.Initobj << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineType << 8 | (byte)StackBehaviour.Popi << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Constrained = new OpCode ( + 0xfe << 0 | 0x16 << 8 | (byte)Code.Constrained << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Prefix << 0 | (byte)OperandType.InlineType << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Cpblk = new OpCode ( + 0xfe << 0 | 0x17 << 8 | (byte)Code.Cpblk << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi_popi_popi << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Initblk = new OpCode ( + 0xfe << 0 | 0x18 << 8 | (byte)Code.Initblk << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Popi_popi_popi << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode No = new OpCode ( + 0xfe << 0 | 0x19 << 8 | (byte)Code.No << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Prefix << 0 | (byte)OperandType.ShortInlineI << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Rethrow = new OpCode ( + 0xfe << 0 | 0x1a << 8 | (byte)Code.Rethrow << 16 | (byte)FlowControl.Throw << 24, + (byte)OpCodeType.Objmodel << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push0 << 24); + + public static readonly OpCode Sizeof = new OpCode ( + 0xfe << 0 | 0x1c << 8 | (byte)Code.Sizeof << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineType << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Refanytype = new OpCode ( + 0xfe << 0 | 0x1d << 8 | (byte)Code.Refanytype << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Primitive << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop1 << 16 | (byte)StackBehaviour.Pushi << 24); + + public static readonly OpCode Readonly = new OpCode ( + 0xfe << 0 | 0x1e << 8 | (byte)Code.Readonly << 16 | (byte)FlowControl.Next << 24, + (byte)OpCodeType.Prefix << 0 | (byte)OperandType.InlineNone << 8 | (byte)StackBehaviour.Pop0 << 16 | (byte)StackBehaviour.Push0 << 24); + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/OpCodes.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/OpCodes.cs.meta new file mode 100644 index 0000000..422968a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/OpCodes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 52c19a62321778c438d2f6b483f18e73 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/PortablePdb.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/PortablePdb.cs new file mode 100644 index 0000000..64b7ad7 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/PortablePdb.cs @@ -0,0 +1,591 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.Metadata; +using MonoFN.Cecil.PE; +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; + +namespace MonoFN.Cecil.Cil { + + public sealed class PortablePdbReaderProvider : ISymbolReaderProvider { + + public ISymbolReader GetSymbolReader (ModuleDefinition module, string fileName) + { + Mixin.CheckModule (module); + Mixin.CheckFileName (fileName); + + var file = File.OpenRead (Mixin.GetPdbFileName (fileName)); + return GetSymbolReader (module, Disposable.Owned (file as Stream), file.Name); + } + + public ISymbolReader GetSymbolReader (ModuleDefinition module, Stream symbolStream) + { + Mixin.CheckModule (module); + Mixin.CheckStream (symbolStream); + + return GetSymbolReader (module, Disposable.NotOwned (symbolStream), symbolStream.GetFileName ()); + } + + ISymbolReader GetSymbolReader (ModuleDefinition module, Disposable symbolStream, string fileName) + { + return new PortablePdbReader (ImageReader.ReadPortablePdb (symbolStream, fileName), module); + } + } + + public sealed class PortablePdbReader : ISymbolReader { + + readonly Image image; + readonly ModuleDefinition module; + readonly MetadataReader reader; + readonly MetadataReader debug_reader; + + bool IsEmbedded { get { return reader.image == debug_reader.image; } } + + internal PortablePdbReader (Image image, ModuleDefinition module) + { + this.image = image; + this.module = module; + this.reader = module.reader; + this.debug_reader = new MetadataReader (image, module, this.reader); + } + + public ISymbolWriterProvider GetWriterProvider () + { + return new PortablePdbWriterProvider (); + } + + public bool ProcessDebugHeader (ImageDebugHeader header) + { + if (image == module.Image) + return true; + + foreach (var entry in header.Entries) { + if (!IsMatchingEntry (image.PdbHeap, entry)) + continue; + + ReadModule (); + return true; + } + + return false; + } + + static bool IsMatchingEntry (PdbHeap heap, ImageDebugHeaderEntry entry) + { + if (entry.Directory.Type != ImageDebugType.CodeView) + return false; + + var data = entry.Data; + + if (data.Length < 24) + return false; + + var magic = ReadInt32 (data, 0); + if (magic != 0x53445352) + return false; + + var buffer = new byte [16]; + Buffer.BlockCopy (data, 4, buffer, 0, 16); + + var module_guid = new Guid (buffer); + + Buffer.BlockCopy (heap.Id, 0, buffer, 0, 16); + + var pdb_guid = new Guid (buffer); + + return module_guid == pdb_guid; + } + + static int ReadInt32 (byte [] bytes, int start) + { + return (bytes [start] + | (bytes [start + 1] << 8) + | (bytes [start + 2] << 16) + | (bytes [start + 3] << 24)); + } + + void ReadModule () + { + module.custom_infos = debug_reader.GetCustomDebugInformation (module); + } + + public MethodDebugInformation Read (MethodDefinition method) + { + var info = new MethodDebugInformation (method); + ReadSequencePoints (info); + ReadScope (info); + ReadStateMachineKickOffMethod (info); + ReadCustomDebugInformations (info); + return info; + } + + void ReadSequencePoints (MethodDebugInformation method_info) + { + method_info.sequence_points = debug_reader.ReadSequencePoints (method_info.method); + } + + void ReadScope (MethodDebugInformation method_info) + { + method_info.scope = debug_reader.ReadScope (method_info.method); + } + + void ReadStateMachineKickOffMethod (MethodDebugInformation method_info) + { + method_info.kickoff_method = debug_reader.ReadStateMachineKickoffMethod (method_info.method); + } + + void ReadCustomDebugInformations (MethodDebugInformation info) + { + info.method.custom_infos = debug_reader.GetCustomDebugInformation (info.method); + } + + public void Dispose () + { + if (IsEmbedded) + return; + + image.Dispose (); + } + } + + public sealed class EmbeddedPortablePdbReaderProvider : ISymbolReaderProvider { + + public ISymbolReader GetSymbolReader (ModuleDefinition module, string fileName) + { + Mixin.CheckModule (module); + + var header = module.GetDebugHeader (); + var entry = header.GetEmbeddedPortablePdbEntry (); + if (entry == null) + throw new InvalidOperationException (); + + return new EmbeddedPortablePdbReader ( + (PortablePdbReader)new PortablePdbReaderProvider ().GetSymbolReader (module, GetPortablePdbStream (entry))); + } + + static Stream GetPortablePdbStream (ImageDebugHeaderEntry entry) + { + var compressed_stream = new MemoryStream (entry.Data); + var reader = new BinaryStreamReader (compressed_stream); + reader.ReadInt32 (); // signature + var length = reader.ReadInt32 (); + var decompressed_stream = new MemoryStream (length); + + using (var deflate_stream = new DeflateStream (compressed_stream, CompressionMode.Decompress, leaveOpen: true)) + deflate_stream.CopyTo (decompressed_stream); + + return decompressed_stream; + } + + public ISymbolReader GetSymbolReader (ModuleDefinition module, Stream symbolStream) + { + throw new NotSupportedException (); + } + } + + public sealed class EmbeddedPortablePdbReader : ISymbolReader { + private readonly PortablePdbReader reader; + + internal EmbeddedPortablePdbReader (PortablePdbReader reader) + { + if (reader == null) + throw new ArgumentNullException (); + + this.reader = reader; + } + + public ISymbolWriterProvider GetWriterProvider () + { + return new EmbeddedPortablePdbWriterProvider (); + } + + public bool ProcessDebugHeader (ImageDebugHeader header) + { + return reader.ProcessDebugHeader (header); + } + + public MethodDebugInformation Read (MethodDefinition method) + { + return reader.Read (method); + } + + public void Dispose () + { + reader.Dispose (); + } + } + + public sealed class PortablePdbWriterProvider : ISymbolWriterProvider { + public ISymbolWriter GetSymbolWriter (ModuleDefinition module, string fileName) + { + Mixin.CheckModule (module); + Mixin.CheckFileName (fileName); + + var file = File.OpenWrite (Mixin.GetPdbFileName (fileName)); + return GetSymbolWriter (module, Disposable.Owned (file as Stream)); + } + + public ISymbolWriter GetSymbolWriter (ModuleDefinition module, Stream symbolStream) + { + Mixin.CheckModule (module); + Mixin.CheckStream (symbolStream); + + return GetSymbolWriter (module, Disposable.NotOwned (symbolStream)); + } + + ISymbolWriter GetSymbolWriter (ModuleDefinition module, Disposable stream) + { + var metadata = new MetadataBuilder (module, this); + var writer = ImageWriter.CreateDebugWriter (module, metadata, stream); + + return new PortablePdbWriter (metadata, module, writer); + } + } + + public sealed class PortablePdbWriter : ISymbolWriter { + + readonly MetadataBuilder pdb_metadata; + readonly ModuleDefinition module; + readonly ImageWriter writer; + + MetadataBuilder module_metadata; + + bool IsEmbedded { get { return writer == null; } } + + internal PortablePdbWriter (MetadataBuilder pdb_metadata, ModuleDefinition module) + { + this.pdb_metadata = pdb_metadata; + this.module = module; + + this.module_metadata = module.metadata_builder; + + if (module_metadata != pdb_metadata) + this.pdb_metadata.metadata_builder = this.module_metadata; + + pdb_metadata.AddCustomDebugInformations (module); + } + + internal PortablePdbWriter (MetadataBuilder pdb_metadata, ModuleDefinition module, ImageWriter writer) + : this (pdb_metadata, module) + { + this.writer = writer; + } + + public ISymbolReaderProvider GetReaderProvider () + { + return new PortablePdbReaderProvider (); + } + + public ImageDebugHeader GetDebugHeader () + { + if (IsEmbedded) + return new ImageDebugHeader (); + + var directory = new ImageDebugDirectory () { + MajorVersion = 256, + MinorVersion = 20557, + Type = ImageDebugType.CodeView, + TimeDateStamp = (int)module.timestamp, + }; + + var buffer = new ByteBuffer (); + // RSDS + buffer.WriteUInt32 (0x53445352); + // Module ID + buffer.WriteBytes (module.Mvid.ToByteArray ()); + // PDB Age + buffer.WriteUInt32 (1); + // PDB Path + var fileName = writer.BaseStream.GetFileName (); + if (string.IsNullOrEmpty (fileName)) { + fileName = module.Assembly.Name.Name + ".pdb"; + } + buffer.WriteBytes (System.Text.Encoding.UTF8.GetBytes (fileName)); + buffer.WriteByte (0); + + var data = new byte [buffer.length]; + Buffer.BlockCopy (buffer.buffer, 0, data, 0, buffer.length); + directory.SizeOfData = data.Length; + + return new ImageDebugHeader (new ImageDebugHeaderEntry (directory, data)); + } + + public void Write (MethodDebugInformation info) + { + CheckMethodDebugInformationTable (); + + pdb_metadata.AddMethodDebugInformation (info); + } + + void CheckMethodDebugInformationTable () + { + var mdi = pdb_metadata.table_heap.GetTable (Table.MethodDebugInformation); + if (mdi.length > 0) + return; + + // The MethodDebugInformation table has the same length as the Method table + mdi.rows = new Row [module_metadata.method_rid - 1]; + mdi.length = mdi.rows.Length; + } + + public void Dispose () + { + if (IsEmbedded) + return; + + WritePdbFile (); + } + + void WritePdbFile () + { + WritePdbHeap (); + + WriteTableHeap (); + + writer.BuildMetadataTextMap (); + writer.WriteMetadataHeader (); + writer.WriteMetadata (); + + writer.Flush (); + writer.stream.Dispose (); + } + + void WritePdbHeap () + { + var pdb_heap = pdb_metadata.pdb_heap; + + pdb_heap.WriteBytes (module.Mvid.ToByteArray ()); + pdb_heap.WriteUInt32 (module_metadata.timestamp); + + pdb_heap.WriteUInt32 (module_metadata.entry_point.ToUInt32 ()); + + var table_heap = module_metadata.table_heap; + var tables = table_heap.tables; + + ulong valid = 0; + for (int i = 0; i < tables.Length; i++) { + if (tables [i] == null || tables [i].Length == 0) + continue; + + valid |= (1UL << i); + } + + pdb_heap.WriteUInt64 (valid); + + for (int i = 0; i < tables.Length; i++) { + if (tables [i] == null || tables [i].Length == 0) + continue; + + pdb_heap.WriteUInt32 ((uint)tables [i].Length); + } + } + + void WriteTableHeap () + { + pdb_metadata.table_heap.string_offsets = pdb_metadata.string_heap.WriteStrings (); + pdb_metadata.table_heap.ComputeTableInformations (); + pdb_metadata.table_heap.WriteTableHeap (); + } + } + + public sealed class EmbeddedPortablePdbWriterProvider : ISymbolWriterProvider { + + public ISymbolWriter GetSymbolWriter (ModuleDefinition module, string fileName) + { + Mixin.CheckModule (module); + Mixin.CheckFileName (fileName); + + var stream = new MemoryStream (); + var pdb_writer = (PortablePdbWriter)new PortablePdbWriterProvider ().GetSymbolWriter (module, stream); + return new EmbeddedPortablePdbWriter (stream, pdb_writer); + } + + public ISymbolWriter GetSymbolWriter (ModuleDefinition module, Stream symbolStream) + { + throw new NotSupportedException (); + } + } + + public sealed class EmbeddedPortablePdbWriter : ISymbolWriter { + + readonly Stream stream; + readonly PortablePdbWriter writer; + + internal EmbeddedPortablePdbWriter (Stream stream, PortablePdbWriter writer) + { + this.stream = stream; + this.writer = writer; + } + + public ISymbolReaderProvider GetReaderProvider () + { + return new EmbeddedPortablePdbReaderProvider (); + } + + public ImageDebugHeader GetDebugHeader () + { + writer.Dispose (); + + var directory = new ImageDebugDirectory { + Type = ImageDebugType.EmbeddedPortablePdb, + MajorVersion = 0x0100, + MinorVersion = 0x0100, + }; + + var data = new MemoryStream (); + + var w = new BinaryStreamWriter (data); + w.WriteByte (0x4d); + w.WriteByte (0x50); + w.WriteByte (0x44); + w.WriteByte (0x42); + + w.WriteInt32 ((int)stream.Length); + + stream.Position = 0; + + using (var compress_stream = new DeflateStream (data, CompressionMode.Compress, leaveOpen: true)) + stream.CopyTo (compress_stream); + + directory.SizeOfData = (int)data.Length; + + return new ImageDebugHeader (new [] { + writer.GetDebugHeader ().Entries [0], + new ImageDebugHeaderEntry (directory, data.ToArray ()) + }); + } + + public void Write (MethodDebugInformation info) + { + writer.Write (info); + } + + public void Dispose () + { + } + } + + static class PdbGuidMapping { + + static readonly Dictionary guid_language = new Dictionary (); + static readonly Dictionary language_guid = new Dictionary (); + + static PdbGuidMapping () + { + AddMapping (DocumentLanguage.C, new Guid ("63a08714-fc37-11d2-904c-00c04fa302a1")); + AddMapping (DocumentLanguage.Cpp, new Guid ("3a12d0b7-c26c-11d0-b442-00a0244a1dd2")); + AddMapping (DocumentLanguage.CSharp, new Guid ("3f5162f8-07c6-11d3-9053-00c04fa302a1")); + AddMapping (DocumentLanguage.Basic, new Guid ("3a12d0b8-c26c-11d0-b442-00a0244a1dd2")); + AddMapping (DocumentLanguage.Java, new Guid ("3a12d0b4-c26c-11d0-b442-00a0244a1dd2")); + AddMapping (DocumentLanguage.Cobol, new Guid ("af046cd1-d0e1-11d2-977c-00a0c9b4d50c")); + AddMapping (DocumentLanguage.Pascal, new Guid ("af046cd2-d0e1-11d2-977c-00a0c9b4d50c")); + AddMapping (DocumentLanguage.Cil, new Guid ("af046cd3-d0e1-11d2-977c-00a0c9b4d50c")); + AddMapping (DocumentLanguage.JScript, new Guid ("3a12d0b6-c26c-11d0-b442-00a0244a1dd2")); + AddMapping (DocumentLanguage.Smc, new Guid ("0d9b9f7b-6611-11d3-bd2a-0000f80849bd")); + AddMapping (DocumentLanguage.MCpp, new Guid ("4b35fde8-07c6-11d3-9053-00c04fa302a1")); + AddMapping (DocumentLanguage.FSharp, new Guid ("ab4f38c9-b6e6-43ba-be3b-58080b2ccce3")); + } + + static void AddMapping (DocumentLanguage language, Guid guid) + { + guid_language.Add (guid, language); + language_guid.Add (language, guid); + } + + static readonly Guid type_text = new Guid ("5a869d0b-6611-11d3-bd2a-0000f80849bd"); + + public static DocumentType ToType (this Guid guid) + { + if (guid == type_text) + return DocumentType.Text; + + return DocumentType.Other; + } + + public static Guid ToGuid (this DocumentType type) + { + if (type == DocumentType.Text) + return type_text; + + return new Guid (); + } + + static readonly Guid hash_md5 = new Guid ("406ea660-64cf-4c82-b6f0-42d48172a799"); + static readonly Guid hash_sha1 = new Guid ("ff1816ec-aa5e-4d10-87f7-6f4963833460"); + static readonly Guid hash_sha256 = new Guid ("8829d00f-11b8-4213-878b-770e8597ac16"); + + public static DocumentHashAlgorithm ToHashAlgorithm (this Guid guid) + { + if (guid == hash_md5) + return DocumentHashAlgorithm.MD5; + + if (guid == hash_sha1) + return DocumentHashAlgorithm.SHA1; + + if (guid == hash_sha256) + return DocumentHashAlgorithm.SHA256; + + return DocumentHashAlgorithm.None; + } + + public static Guid ToGuid (this DocumentHashAlgorithm hash_algo) + { + if (hash_algo == DocumentHashAlgorithm.MD5) + return hash_md5; + + if (hash_algo == DocumentHashAlgorithm.SHA1) + return hash_sha1; + + if (hash_algo == DocumentHashAlgorithm.SHA256) + return hash_sha256; + + return new Guid (); + } + + public static DocumentLanguage ToLanguage (this Guid guid) + { + DocumentLanguage language; + if (!guid_language.TryGetValue (guid, out language)) + return DocumentLanguage.Other; + + return language; + } + + public static Guid ToGuid (this DocumentLanguage language) + { + Guid guid; + if (!language_guid.TryGetValue (language, out guid)) + return new Guid (); + + return guid; + } + + static readonly Guid vendor_ms = new Guid ("994b45c4-e6e9-11d2-903f-00c04fa302a1"); + + public static DocumentLanguageVendor ToVendor (this Guid guid) + { + if (guid == vendor_ms) + return DocumentLanguageVendor.Microsoft; + + return DocumentLanguageVendor.Other; + } + + public static Guid ToGuid (this DocumentLanguageVendor vendor) + { + if (vendor == DocumentLanguageVendor.Microsoft) + return vendor_ms; + + return new Guid (); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/PortablePdb.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/PortablePdb.cs.meta new file mode 100644 index 0000000..8b260c9 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/PortablePdb.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 34e2f5d4b9b9ea542bec15f2d9acc3ed +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/SequencePoint.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/SequencePoint.cs new file mode 100644 index 0000000..591815a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/SequencePoint.cs @@ -0,0 +1,76 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil.Cil { + + public sealed class SequencePoint { + + internal InstructionOffset offset; + Document document; + + int start_line; + int start_column; + int end_line; + int end_column; + + public int Offset { + get { return offset.Offset; } + } + + public int StartLine { + get { return start_line; } + set { start_line = value; } + } + + public int StartColumn { + get { return start_column; } + set { start_column = value; } + } + + public int EndLine { + get { return end_line; } + set { end_line = value; } + } + + public int EndColumn { + get { return end_column; } + set { end_column = value; } + } + + public bool IsHidden { + get { return start_line == 0xfeefee && start_line == end_line; } + } + + public Document Document { + get { return document; } + set { document = value; } + } + + internal SequencePoint (int offset, Document document) + { + if (document == null) + throw new ArgumentNullException ("document"); + + this.offset = new InstructionOffset (offset); + this.document = document; + } + + public SequencePoint (Instruction instruction, Document document) + { + if (document == null) + throw new ArgumentNullException ("document"); + + this.offset = new InstructionOffset (instruction); + this.document = document; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/SequencePoint.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/SequencePoint.cs.meta new file mode 100644 index 0000000..bac78ab --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/SequencePoint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 612e6675dca63a84b9e8aaeb3e3b9ec6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Symbols.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Symbols.cs new file mode 100644 index 0000000..4e75a0c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Symbols.cs @@ -0,0 +1,1226 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.Cil; +using MonoFN.Cecil.PE; +using MonoFN.Collections.Generic; +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; +using SR = System.Reflection; + +namespace MonoFN.Cecil.Cil { + + [StructLayout (LayoutKind.Sequential)] + public struct ImageDebugDirectory { + public const int Size = 28; + + public int Characteristics; + public int TimeDateStamp; + public short MajorVersion; + public short MinorVersion; + public ImageDebugType Type; + public int SizeOfData; + public int AddressOfRawData; + public int PointerToRawData; + } + + public enum ImageDebugType { + CodeView = 2, + Deterministic = 16, + EmbeddedPortablePdb = 17, + } + + public sealed class ImageDebugHeader { + + readonly ImageDebugHeaderEntry [] entries; + + public bool HasEntries { + get { return !entries.IsNullOrEmpty (); } + } + + public ImageDebugHeaderEntry [] Entries { + get { return entries; } + } + + public ImageDebugHeader (ImageDebugHeaderEntry [] entries) + { + this.entries = entries ?? Empty.Array; + } + + public ImageDebugHeader () + : this (Empty.Array) + { + } + + public ImageDebugHeader (ImageDebugHeaderEntry entry) + : this (new [] { entry }) + { + } + } + + public sealed class ImageDebugHeaderEntry { + + ImageDebugDirectory directory; + readonly byte [] data; + + public ImageDebugDirectory Directory { + get { return directory; } + internal set { directory = value; } + } + + public byte [] Data { + get { return data; } + } + + public ImageDebugHeaderEntry (ImageDebugDirectory directory, byte [] data) + { + this.directory = directory; + this.data = data ?? Empty.Array; + } + } + + public sealed class ScopeDebugInformation : DebugInformation { + + internal InstructionOffset start; + internal InstructionOffset end; + internal ImportDebugInformation import; + internal Collection scopes; + internal Collection variables; + internal Collection constants; + + public InstructionOffset Start { + get { return start; } + set { start = value; } + } + + public InstructionOffset End { + get { return end; } + set { end = value; } + } + + public ImportDebugInformation Import { + get { return import; } + set { import = value; } + } + + public bool HasScopes { + get { return !scopes.IsNullOrEmpty (); } + } + + public Collection Scopes { + get { + if (scopes == null) + Interlocked.CompareExchange (ref scopes, new Collection (), null); + + return scopes; + } + } + + public bool HasVariables { + get { return !variables.IsNullOrEmpty (); } + } + + public Collection Variables { + get { + if (variables == null) + Interlocked.CompareExchange (ref variables, new Collection (), null); + + return variables; + } + } + + public bool HasConstants { + get { return !constants.IsNullOrEmpty (); } + } + + public Collection Constants { + get { + if (constants == null) + Interlocked.CompareExchange (ref constants, new Collection (), null); + + return constants; + } + } + + internal ScopeDebugInformation () + { + this.token = new MetadataToken (TokenType.LocalScope); + } + + public ScopeDebugInformation (Instruction start, Instruction end) + : this () + { + if (start == null) + throw new ArgumentNullException ("start"); + + this.start = new InstructionOffset (start); + + if (end != null) + this.end = new InstructionOffset (end); + } + + public bool TryGetName (VariableDefinition variable, out string name) + { + name = null; + if (variables == null || variables.Count == 0) + return false; + + for (int i = 0; i < variables.Count; i++) { + if (variables [i].Index == variable.Index) { + name = variables [i].Name; + return true; + } + } + + return false; + } + } + + public struct InstructionOffset { + + readonly Instruction instruction; + readonly int? offset; + + public int Offset { + get { + if (instruction != null) + return instruction.Offset; + if (offset.HasValue) + return offset.Value; + + throw new NotSupportedException (); + } + } + + public bool IsEndOfMethod { + get { return instruction == null && !offset.HasValue; } + } + + internal bool IsResolved => instruction != null || !offset.HasValue; + + internal Instruction ResolvedInstruction => instruction; + + public InstructionOffset (Instruction instruction) + { + if (instruction == null) + throw new ArgumentNullException ("instruction"); + + this.instruction = instruction; + this.offset = null; + } + + public InstructionOffset (int offset) + { + this.instruction = null; + this.offset = offset; + } + } + + [Flags] + public enum VariableAttributes : ushort { + None = 0, + DebuggerHidden = 1, + } + + public struct VariableIndex { + readonly VariableDefinition variable; + readonly int? index; + + public int Index { + get { + if (variable != null) + return variable.Index; + if (index.HasValue) + return index.Value; + + throw new NotSupportedException (); + } + } + + internal bool IsResolved => variable != null; + + internal VariableDefinition ResolvedVariable => variable; + + public VariableIndex (VariableDefinition variable) + { + if (variable == null) + throw new ArgumentNullException ("variable"); + + this.variable = variable; + this.index = null; + } + + public VariableIndex (int index) + { + this.variable = null; + this.index = index; + } + } + + public abstract class DebugInformation : ICustomDebugInformationProvider { + + internal MetadataToken token; + internal Collection custom_infos; + + public MetadataToken MetadataToken { + get { return token; } + set { token = value; } + } + + public bool HasCustomDebugInformations { + get { return !custom_infos.IsNullOrEmpty (); } + } + + public Collection CustomDebugInformations { + get { + if (custom_infos == null) + Interlocked.CompareExchange (ref custom_infos, new Collection (), null); + + return custom_infos; + } + } + + internal DebugInformation () + { + } + } + + public sealed class VariableDebugInformation : DebugInformation { + + string name; + ushort attributes; + internal VariableIndex index; + + public int Index { + get { return index.Index; } + } + + public string Name { + get { return name; } + set { name = value; } + } + + public VariableAttributes Attributes { + get { return (VariableAttributes)attributes; } + set { attributes = (ushort)value; } + } + + public bool IsDebuggerHidden { + get { return attributes.GetAttributes ((ushort)VariableAttributes.DebuggerHidden); } + set { attributes = attributes.SetAttributes ((ushort)VariableAttributes.DebuggerHidden, value); } + } + + internal VariableDebugInformation (int index, string name) + { + if (name == null) + throw new ArgumentNullException ("name"); + + this.index = new VariableIndex (index); + this.name = name; + } + + public VariableDebugInformation (VariableDefinition variable, string name) + { + if (variable == null) + throw new ArgumentNullException ("variable"); + if (name == null) + throw new ArgumentNullException ("name"); + + this.index = new VariableIndex (variable); + this.name = name; + this.token = new MetadataToken (TokenType.LocalVariable); + } + } + + public sealed class ConstantDebugInformation : DebugInformation { + + string name; + TypeReference constant_type; + object value; + + public string Name { + get { return name; } + set { name = value; } + } + + public TypeReference ConstantType { + get { return constant_type; } + set { constant_type = value; } + } + + public object Value { + get { return value; } + set { this.value = value; } + } + + public ConstantDebugInformation (string name, TypeReference constant_type, object value) + { + if (name == null) + throw new ArgumentNullException ("name"); + + this.name = name; + this.constant_type = constant_type; + this.value = value; + this.token = new MetadataToken (TokenType.LocalConstant); + } + } + + public enum ImportTargetKind : byte { + ImportNamespace = 1, + ImportNamespaceInAssembly = 2, + ImportType = 3, + ImportXmlNamespaceWithAlias = 4, + ImportAlias = 5, + DefineAssemblyAlias = 6, + DefineNamespaceAlias = 7, + DefineNamespaceInAssemblyAlias = 8, + DefineTypeAlias = 9, + } + + public sealed class ImportTarget { + + internal ImportTargetKind kind; + + internal string @namespace; + internal TypeReference type; + internal AssemblyNameReference reference; + internal string alias; + + public string Namespace { + get { return @namespace; } + set { @namespace = value; } + } + + public TypeReference Type { + get { return type; } + set { type = value; } + } + + public AssemblyNameReference AssemblyReference { + get { return reference; } + set { reference = value; } + } + + public string Alias { + get { return alias; } + set { alias = value; } + } + + public ImportTargetKind Kind { + get { return kind; } + set { kind = value; } + } + + public ImportTarget (ImportTargetKind kind) + { + this.kind = kind; + } + } + + public sealed class ImportDebugInformation : DebugInformation { + + internal ImportDebugInformation parent; + internal Collection targets; + + public bool HasTargets { + get { return !targets.IsNullOrEmpty (); } + } + + public Collection Targets { + get { + if (targets == null) + Interlocked.CompareExchange (ref targets, new Collection (), null); + + return targets; + } + } + + public ImportDebugInformation Parent { + get { return parent; } + set { parent = value; } + } + + public ImportDebugInformation () + { + this.token = new MetadataToken (TokenType.ImportScope); + } + } + + public interface ICustomDebugInformationProvider : IMetadataTokenProvider { + bool HasCustomDebugInformations { get; } + Collection CustomDebugInformations { get; } + } + + public enum CustomDebugInformationKind { + Binary, + StateMachineScope, + DynamicVariable, + DefaultNamespace, + AsyncMethodBody, + EmbeddedSource, + SourceLink, + } + + public abstract class CustomDebugInformation : DebugInformation { + + Guid identifier; + + public Guid Identifier { + get { return identifier; } + } + + public abstract CustomDebugInformationKind Kind { get; } + + internal CustomDebugInformation (Guid identifier) + { + this.identifier = identifier; + this.token = new MetadataToken (TokenType.CustomDebugInformation); + } + } + + public sealed class BinaryCustomDebugInformation : CustomDebugInformation { + + byte [] data; + + public byte [] Data { + get { return data; } + set { data = value; } + } + + public override CustomDebugInformationKind Kind { + get { return CustomDebugInformationKind.Binary; } + } + + public BinaryCustomDebugInformation (Guid identifier, byte [] data) + : base (identifier) + { + this.data = data; + } + } + + public sealed class AsyncMethodBodyDebugInformation : CustomDebugInformation { + + internal InstructionOffset catch_handler; + internal Collection yields; + internal Collection resumes; + internal Collection resume_methods; + + public InstructionOffset CatchHandler { + get { return catch_handler; } + set { catch_handler = value; } + } + + public Collection Yields { + get { + if (yields == null) + Interlocked.CompareExchange (ref yields, new Collection (), null); + + return yields; + } + } + + public Collection Resumes { + get { + if (resumes == null) + Interlocked.CompareExchange (ref resumes, new Collection (), null); + + return resumes; + } + } + + public Collection ResumeMethods { + get { return resume_methods ?? (resume_methods = new Collection ()); } + } + + public override CustomDebugInformationKind Kind { + get { return CustomDebugInformationKind.AsyncMethodBody; } + } + + public static Guid KindIdentifier = new Guid ("{54FD2AC5-E925-401A-9C2A-F94F171072F8}"); + + internal AsyncMethodBodyDebugInformation (int catchHandler) + : base (KindIdentifier) + { + this.catch_handler = new InstructionOffset (catchHandler); + } + + public AsyncMethodBodyDebugInformation (Instruction catchHandler) + : base (KindIdentifier) + { + this.catch_handler = new InstructionOffset (catchHandler); + } + + public AsyncMethodBodyDebugInformation () + : base (KindIdentifier) + { + this.catch_handler = new InstructionOffset (-1); + } + } + + public sealed class StateMachineScope { + + internal InstructionOffset start; + internal InstructionOffset end; + + public InstructionOffset Start { + get { return start; } + set { start = value; } + } + + public InstructionOffset End { + get { return end; } + set { end = value; } + } + + internal StateMachineScope (int start, int end) + { + this.start = new InstructionOffset (start); + this.end = new InstructionOffset (end); + } + + public StateMachineScope (Instruction start, Instruction end) + { + this.start = new InstructionOffset (start); + this.end = end != null ? new InstructionOffset (end) : new InstructionOffset (); + } + } + + public sealed class StateMachineScopeDebugInformation : CustomDebugInformation { + + internal Collection scopes; + + public Collection Scopes { + get { return scopes ?? (scopes = new Collection ()); } + } + + public override CustomDebugInformationKind Kind { + get { return CustomDebugInformationKind.StateMachineScope; } + } + + public static Guid KindIdentifier = new Guid ("{6DA9A61E-F8C7-4874-BE62-68BC5630DF71}"); + + public StateMachineScopeDebugInformation () + : base (KindIdentifier) + { + } + } + + public sealed class EmbeddedSourceDebugInformation : CustomDebugInformation { + + internal uint index; + internal MetadataReader debug_reader; + internal bool resolved; + internal byte [] content; + internal bool compress; + + public byte [] Content { + get { + if (!resolved) + Resolve (); + + return content; + } + set { + content = value; + resolved = true; + } + } + + public bool Compress { + get { + if (!resolved) + Resolve (); + + return compress; + } + set { + compress = value; + resolved = true; + } + } + + public override CustomDebugInformationKind Kind { + get { return CustomDebugInformationKind.EmbeddedSource; } + } + + public static Guid KindIdentifier = new Guid ("{0E8A571B-6926-466E-B4AD-8AB04611F5FE}"); + + internal EmbeddedSourceDebugInformation (uint index, MetadataReader debug_reader) + : base (KindIdentifier) + { + this.index = index; + this.debug_reader = debug_reader; + } + + public EmbeddedSourceDebugInformation (byte [] content, bool compress) + : base (KindIdentifier) + { + this.resolved = true; + this.content = content; + this.compress = compress; + } + + internal byte [] ReadRawEmbeddedSourceDebugInformation () + { + if (debug_reader == null) + throw new InvalidOperationException (); + + return debug_reader.ReadRawEmbeddedSourceDebugInformation (index); + } + + void Resolve () + { + if (resolved) + return; + + if (debug_reader == null) + throw new InvalidOperationException (); + + var row = debug_reader.ReadEmbeddedSourceDebugInformation (index); + content = row.Col1; + compress = row.Col2; + resolved = true; + } + } + + public sealed class SourceLinkDebugInformation : CustomDebugInformation { + + internal string content; + + public string Content { + get { return content; } + set { content = value; } + } + + public override CustomDebugInformationKind Kind { + get { return CustomDebugInformationKind.SourceLink; } + } + + public static Guid KindIdentifier = new Guid ("{CC110556-A091-4D38-9FEC-25AB9A351A6A}"); + + public SourceLinkDebugInformation (string content) + : base (KindIdentifier) + { + this.content = content; + } + } + + public sealed class MethodDebugInformation : DebugInformation { + + internal MethodDefinition method; + internal Collection sequence_points; + internal ScopeDebugInformation scope; + internal MethodDefinition kickoff_method; + internal int code_size; + internal MetadataToken local_var_token; + + public MethodDefinition Method { + get { return method; } + } + + public bool HasSequencePoints { + get { return !sequence_points.IsNullOrEmpty (); } + } + + public Collection SequencePoints { + get { + if (sequence_points == null) + Interlocked.CompareExchange (ref sequence_points, new Collection (), null); + + return sequence_points; + } + } + + public ScopeDebugInformation Scope { + get { return scope; } + set { scope = value; } + } + + public MethodDefinition StateMachineKickOffMethod { + get { return kickoff_method; } + set { kickoff_method = value; } + } + + internal MethodDebugInformation (MethodDefinition method) + { + if (method == null) + throw new ArgumentNullException ("method"); + + this.method = method; + this.token = new MetadataToken (TokenType.MethodDebugInformation, method.MetadataToken.RID); + } + + public SequencePoint GetSequencePoint (Instruction instruction) + { + if (!HasSequencePoints) + return null; + + for (int i = 0; i < sequence_points.Count; i++) + if (sequence_points [i].Offset == instruction.Offset) + return sequence_points [i]; + + return null; + } + + public IDictionary GetSequencePointMapping () + { + var instruction_mapping = new Dictionary (); + if (!HasSequencePoints || !method.HasBody) + return instruction_mapping; + + var offset_mapping = new Dictionary (sequence_points.Count); + + for (int i = 0; i < sequence_points.Count; i++) { + if (!offset_mapping.ContainsKey (sequence_points [i].Offset)) + offset_mapping.Add (sequence_points [i].Offset, sequence_points [i]); + } + + var instructions = method.Body.Instructions; + + for (int i = 0; i < instructions.Count; i++) { + SequencePoint sequence_point; + if (offset_mapping.TryGetValue (instructions [i].Offset, out sequence_point)) + instruction_mapping.Add (instructions [i], sequence_point); + } + + return instruction_mapping; + } + + public IEnumerable GetScopes () + { + if (scope == null) + return Empty.Array; + + return GetScopes (new [] { scope }); + } + + static IEnumerable GetScopes (IList scopes) + { + for (int i = 0; i < scopes.Count; i++) { + var scope = scopes [i]; + + yield return scope; + + if (!scope.HasScopes) + continue; + + foreach (var sub_scope in GetScopes (scope.Scopes)) + yield return sub_scope; + } + } + + public bool TryGetName (VariableDefinition variable, out string name) + { + name = null; + + var has_name = false; + var unique_name = ""; + + foreach (var scope in GetScopes ()) { + string slot_name; + if (!scope.TryGetName (variable, out slot_name)) + continue; + + if (!has_name) { + has_name = true; + unique_name = slot_name; + continue; + } + + if (unique_name != slot_name) + return false; + } + + name = unique_name; + return has_name; + } + } + + public interface ISymbolReader : IDisposable { + + ISymbolWriterProvider GetWriterProvider (); + bool ProcessDebugHeader (ImageDebugHeader header); + MethodDebugInformation Read (MethodDefinition method); + } + + public interface ISymbolReaderProvider { + ISymbolReader GetSymbolReader (ModuleDefinition module, string fileName); + ISymbolReader GetSymbolReader (ModuleDefinition module, Stream symbolStream); + } + +#if !NET_CORE + [Serializable] +#endif + public sealed class SymbolsNotFoundException : FileNotFoundException { + + public SymbolsNotFoundException (string message) : base (message) + { + } + +#if !NET_CORE + SymbolsNotFoundException ( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) + : base (info, context) + { + } +#endif + } + +#if !NET_CORE + [Serializable] +#endif + public sealed class SymbolsNotMatchingException : InvalidOperationException { + + public SymbolsNotMatchingException (string message) : base (message) + { + } + +#if !NET_CORE + SymbolsNotMatchingException ( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) + : base (info, context) + { + } +#endif + } + + public class DefaultSymbolReaderProvider : ISymbolReaderProvider { + + readonly bool throw_if_no_symbol; + + public DefaultSymbolReaderProvider () + : this (throwIfNoSymbol: true) + { + } + + public DefaultSymbolReaderProvider (bool throwIfNoSymbol) + { + throw_if_no_symbol = throwIfNoSymbol; + } + + public ISymbolReader GetSymbolReader (ModuleDefinition module, string fileName) + { + if (module.Image.HasDebugTables ()) + return null; + + if (module.HasDebugHeader) { + var header = module.GetDebugHeader (); + var entry = header.GetEmbeddedPortablePdbEntry (); + if (entry != null) + return new EmbeddedPortablePdbReaderProvider ().GetSymbolReader (module, fileName); + } + + var pdb_file_name = Mixin.GetPdbFileName (fileName); + + if (File.Exists (pdb_file_name)) { + if (Mixin.IsPortablePdb (Mixin.GetPdbFileName (fileName))) + return new PortablePdbReaderProvider ().GetSymbolReader (module, fileName); + + try { + return SymbolProvider.GetReaderProvider (SymbolKind.NativePdb).GetSymbolReader (module, fileName); + } + catch (Exception) { + // We might not include support for native pdbs. + } + } + + var mdb_file_name = Mixin.GetMdbFileName (fileName); + if (File.Exists (mdb_file_name)) { + try { + return SymbolProvider.GetReaderProvider (SymbolKind.Mdb).GetSymbolReader (module, fileName); + } + catch (Exception) { + // We might not include support for mdbs. + } + } + + if (throw_if_no_symbol) + throw new SymbolsNotFoundException (string.Format ("No symbol found for file: {0}", fileName)); + + return null; + } + + public ISymbolReader GetSymbolReader (ModuleDefinition module, Stream symbolStream) + { + if (module.Image.HasDebugTables ()) + return null; + + if (module.HasDebugHeader) { + var header = module.GetDebugHeader (); + var entry = header.GetEmbeddedPortablePdbEntry (); + if (entry != null) + return new EmbeddedPortablePdbReaderProvider ().GetSymbolReader (module, ""); + } + + Mixin.CheckStream (symbolStream); + Mixin.CheckReadSeek (symbolStream); + + var position = symbolStream.Position; + + const int portablePdbHeader = 0x424a5342; + + var reader = new BinaryStreamReader (symbolStream); + var intHeader = reader.ReadInt32 (); + symbolStream.Position = position; + + if (intHeader == portablePdbHeader) { + return new PortablePdbReaderProvider ().GetSymbolReader (module, symbolStream); + } + + const string nativePdbHeader = "Microsoft C/C++ MSF 7.00"; + + var bytesHeader = reader.ReadBytes (nativePdbHeader.Length); + symbolStream.Position = position; + var isNativePdb = true; + + for (var i = 0; i < bytesHeader.Length; i++) { + if (bytesHeader [i] != (byte)nativePdbHeader [i]) { + isNativePdb = false; + break; + } + } + + if (isNativePdb) { + try { + return SymbolProvider.GetReaderProvider (SymbolKind.NativePdb).GetSymbolReader (module, symbolStream); + } + catch (Exception) { + // We might not include support for native pdbs. + } + } + + const long mdbHeader = 0x45e82623fd7fa614; + + var longHeader = reader.ReadInt64 (); + symbolStream.Position = position; + + if (longHeader == mdbHeader) { + try { + return SymbolProvider.GetReaderProvider (SymbolKind.Mdb).GetSymbolReader (module, symbolStream); + } + catch (Exception) { + // We might not include support for mdbs. + } + } + + if (throw_if_no_symbol) + throw new SymbolsNotFoundException (string.Format ("No symbols found in stream")); + + return null; + } + } + + enum SymbolKind { + NativePdb, + PortablePdb, + EmbeddedPortablePdb, + Mdb, + } + + static class SymbolProvider { + + static SR.AssemblyName GetSymbolAssemblyName (SymbolKind kind) + { + if (kind == SymbolKind.PortablePdb) + throw new ArgumentException (); + + var suffix = GetSymbolNamespace (kind); + + var cecil_name = typeof (SymbolProvider).Assembly.GetName (); + + var name = new SR.AssemblyName { + Name = cecil_name.Name + "." + suffix, + Version = cecil_name.Version, +#if NET_CORE + CultureName = cecil_name.CultureName, +#else + CultureInfo = cecil_name.CultureInfo, +#endif + }; + + name.SetPublicKeyToken (cecil_name.GetPublicKeyToken ()); + + return name; + } + + static Type GetSymbolType (SymbolKind kind, string fullname) + { + var type = Type.GetType (fullname); + if (type != null) + return type; + + var assembly_name = GetSymbolAssemblyName (kind); + + type = Type.GetType (fullname + ", " + assembly_name.FullName); + if (type != null) + return type; + + try { + var assembly = SR.Assembly.Load (assembly_name); + if (assembly != null) + return assembly.GetType (fullname); + } + catch (FileNotFoundException) { + } + catch (FileLoadException) { + } + + return null; + } + + public static ISymbolReaderProvider GetReaderProvider (SymbolKind kind) + { + if (kind == SymbolKind.PortablePdb) + return new PortablePdbReaderProvider (); + if (kind == SymbolKind.EmbeddedPortablePdb) + return new EmbeddedPortablePdbReaderProvider (); + + var provider_name = GetSymbolTypeName (kind, "ReaderProvider"); + var type = GetSymbolType (kind, provider_name); + if (type == null) + throw new TypeLoadException ("Could not find symbol provider type " + provider_name); + + return (ISymbolReaderProvider)Activator.CreateInstance (type); + } + + static string GetSymbolTypeName (SymbolKind kind, string name) + { + return "MonoFN.Cecil" + "." + GetSymbolNamespace (kind) + "." + kind + name; + } + + static string GetSymbolNamespace (SymbolKind kind) + { + if (kind == SymbolKind.PortablePdb || kind == SymbolKind.EmbeddedPortablePdb) + return "Cil"; + if (kind == SymbolKind.NativePdb) + return "Pdb"; + if (kind == SymbolKind.Mdb) + return "Mdb"; + + throw new ArgumentException (); + } + } + + public interface ISymbolWriter : IDisposable { + + ISymbolReaderProvider GetReaderProvider (); + ImageDebugHeader GetDebugHeader (); + void Write (MethodDebugInformation info); + } + + public interface ISymbolWriterProvider { + + ISymbolWriter GetSymbolWriter (ModuleDefinition module, string fileName); + ISymbolWriter GetSymbolWriter (ModuleDefinition module, Stream symbolStream); + } + + public class DefaultSymbolWriterProvider : ISymbolWriterProvider { + + public ISymbolWriter GetSymbolWriter (ModuleDefinition module, string fileName) + { + var reader = module.SymbolReader; + if (reader == null) + throw new InvalidOperationException (); + + if (module.Image != null && module.Image.HasDebugTables ()) + return null; + + return reader.GetWriterProvider ().GetSymbolWriter (module, fileName); + } + + public ISymbolWriter GetSymbolWriter (ModuleDefinition module, Stream symbolStream) + { + throw new NotSupportedException (); + } + } +} + +namespace MonoFN.Cecil { + + static partial class Mixin { + + public static ImageDebugHeaderEntry GetCodeViewEntry (this ImageDebugHeader header) + { + return GetEntry (header, ImageDebugType.CodeView); + } + + public static ImageDebugHeaderEntry GetDeterministicEntry (this ImageDebugHeader header) + { + return GetEntry (header, ImageDebugType.Deterministic); + } + + public static ImageDebugHeader AddDeterministicEntry (this ImageDebugHeader header) + { + var entry = new ImageDebugHeaderEntry (new ImageDebugDirectory { Type = ImageDebugType.Deterministic }, Empty.Array); + if (header == null) + return new ImageDebugHeader (entry); + + var entries = new ImageDebugHeaderEntry [header.Entries.Length + 1]; + Array.Copy (header.Entries, entries, header.Entries.Length); + entries [entries.Length - 1] = entry; + return new ImageDebugHeader (entries); + } + + public static ImageDebugHeaderEntry GetEmbeddedPortablePdbEntry (this ImageDebugHeader header) + { + return GetEntry (header, ImageDebugType.EmbeddedPortablePdb); + } + + private static ImageDebugHeaderEntry GetEntry (this ImageDebugHeader header, ImageDebugType type) + { + if (!header.HasEntries) + return null; + + for (var i = 0; i < header.Entries.Length; i++) { + var entry = header.Entries [i]; + if (entry.Directory.Type == type) + return entry; + } + + return null; + } + + public static string GetPdbFileName (string assemblyFileName) + { + return Path.ChangeExtension (assemblyFileName, ".pdb"); + } + + public static string GetMdbFileName (string assemblyFileName) + { + return assemblyFileName + ".mdb"; + } + + public static bool IsPortablePdb (string fileName) + { + using (var file = new FileStream (fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + return IsPortablePdb (file); + } + + public static bool IsPortablePdb (Stream stream) + { + const uint ppdb_signature = 0x424a5342; + + if (stream.Length < 4) return false; + var position = stream.Position; + try { + var reader = new BinaryReader (stream); + return reader.ReadUInt32 () == ppdb_signature; + } + finally { + stream.Position = position; + } + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Symbols.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Symbols.cs.meta new file mode 100644 index 0000000..f82ee9b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Symbols.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: de9ae158807f471449a81d9b8b3e3c4e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/VariableDefinition.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/VariableDefinition.cs new file mode 100644 index 0000000..b3da0a2 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/VariableDefinition.cs @@ -0,0 +1,29 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil.Cil { + + public sealed class VariableDefinition : VariableReference { + + public bool IsPinned { + get { return variable_type.IsPinned; } + } + + public VariableDefinition (TypeReference variableType) + : base (variableType) + { + } + + public override VariableDefinition Resolve () + { + return this; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/VariableDefinition.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/VariableDefinition.cs.meta new file mode 100644 index 0000000..b4e1886 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/VariableDefinition.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1346fa5ba8ac9e8418cc20d2d7d0ad21 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/VariableReference.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/VariableReference.cs new file mode 100644 index 0000000..1957d8d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/VariableReference.cs @@ -0,0 +1,42 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil.Cil { + + public abstract class VariableReference { + + internal int index = -1; + protected TypeReference variable_type; + + public TypeReference VariableType { + get { return variable_type; } + set { variable_type = value; } + } + + public int Index { + get { return index; } + } + + internal VariableReference (TypeReference variable_type) + { + this.variable_type = variable_type; + } + + public abstract VariableDefinition Resolve (); + + public override string ToString () + { + if (index >= 0) + return "V_" + index; + + return string.Empty; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/VariableReference.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/VariableReference.cs.meta new file mode 100644 index 0000000..e951cc3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/VariableReference.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b5dca1eaac23bfd4ba8e881302a72e33 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata.meta new file mode 100644 index 0000000..8b4cde1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8b0e9c3e91af12a4db473e2b25f2d8bd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/BlobHeap.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/BlobHeap.cs new file mode 100644 index 0000000..63dcd4d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/BlobHeap.cs @@ -0,0 +1,54 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil.Metadata { + + sealed class BlobHeap : Heap { + + public BlobHeap (byte [] data) + : base (data) + { + } + + public byte [] Read (uint index) + { + if (index == 0 || index > this.data.Length - 1) + return Empty.Array; + + int position = (int)index; + int length = (int)data.ReadCompressedUInt32 (ref position); + + if (length > data.Length - position) + return Empty.Array; + + var buffer = new byte [length]; + + Buffer.BlockCopy (data, position, buffer, 0, length); + + return buffer; + } + + public void GetView (uint signature, out byte [] buffer, out int index, out int length) + { + if (signature == 0 || signature > data.Length - 1) { + buffer = null; + index = length = 0; + return; + } + + buffer = data; + + index = (int)signature; + length = (int)buffer.ReadCompressedUInt32 (ref index); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/BlobHeap.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/BlobHeap.cs.meta new file mode 100644 index 0000000..b6ef813 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/BlobHeap.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 33eef67f4a74e1c40a6bdb3da9d22d00 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Buffers.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Buffers.cs new file mode 100644 index 0000000..7ab352c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Buffers.cs @@ -0,0 +1,499 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.PE; +using System; +using System.Collections.Generic; +using System.Text; +using RVA = System.UInt32; + +namespace MonoFN.Cecil.Metadata { + + sealed class TableHeapBuffer : HeapBuffer { + + readonly ModuleDefinition module; + readonly MetadataBuilder metadata; + + readonly internal TableInformation [] table_infos = new TableInformation [Mixin.TableCount]; + readonly internal MetadataTable [] tables = new MetadataTable [Mixin.TableCount]; + + bool large_string; + bool large_blob; + bool large_guid; + + readonly int [] coded_index_sizes = new int [Mixin.CodedIndexCount]; + readonly Func counter; + + internal uint [] string_offsets; + + public override bool IsEmpty { + get { return false; } + } + + public TableHeapBuffer (ModuleDefinition module, MetadataBuilder metadata) + : base (24) + { + this.module = module; + this.metadata = metadata; + this.counter = GetTableLength; + } + + int GetTableLength (Table table) + { + return (int)table_infos [(int)table].Length; + } + + public TTable GetTable (Table table) where TTable : MetadataTable, new() + { + var md_table = (TTable)tables [(int)table]; + if (md_table != null) + return md_table; + + md_table = new TTable (); + tables [(int)table] = md_table; + return md_table; + } + + public void WriteBySize (uint value, int size) + { + if (size == 4) + WriteUInt32 (value); + else + WriteUInt16 ((ushort)value); + } + + public void WriteBySize (uint value, bool large) + { + if (large) + WriteUInt32 (value); + else + WriteUInt16 ((ushort)value); + } + + public void WriteString (uint @string) + { + WriteBySize (string_offsets [@string], large_string); + } + + public void WriteBlob (uint blob) + { + WriteBySize (blob, large_blob); + } + + public void WriteGuid (uint guid) + { + WriteBySize (guid, large_guid); + } + + public void WriteRID (uint rid, Table table) + { + WriteBySize (rid, table_infos [(int)table].IsLarge); + } + + int GetCodedIndexSize (CodedIndex coded_index) + { + var index = (int)coded_index; + var size = coded_index_sizes [index]; + if (size != 0) + return size; + + return coded_index_sizes [index] = coded_index.GetSize (counter); + } + + public void WriteCodedRID (uint rid, CodedIndex coded_index) + { + WriteBySize (rid, GetCodedIndexSize (coded_index)); + } + + public void WriteTableHeap () + { + WriteUInt32 (0); // Reserved + WriteByte (GetTableHeapVersion ()); // MajorVersion + WriteByte (0); // MinorVersion + WriteByte (GetHeapSizes ()); // HeapSizes + WriteByte (10); // Reserved2 + WriteUInt64 (GetValid ()); // Valid + WriteUInt64 (0xc416003301fa00); // Sorted + + WriteRowCount (); + WriteTables (); + } + + void WriteRowCount () + { + for (int i = 0; i < tables.Length; i++) { + var table = tables [i]; + if (table == null || table.Length == 0) + continue; + + WriteUInt32 ((uint)table.Length); + } + } + + void WriteTables () + { + for (int i = 0; i < tables.Length; i++) { + var table = tables [i]; + if (table == null || table.Length == 0) + continue; + + table.Write (this); + } + } + + ulong GetValid () + { + ulong valid = 0; + + for (int i = 0; i < tables.Length; i++) { + var table = tables [i]; + if (table == null || table.Length == 0) + continue; + + table.Sort (); + valid |= (1UL << i); + } + + return valid; + } + + public void ComputeTableInformations () + { + if (metadata.metadata_builder != null) + ComputeTableInformations (metadata.metadata_builder.table_heap); + + ComputeTableInformations (metadata.table_heap); + } + + void ComputeTableInformations (TableHeapBuffer table_heap) + { + var tables = table_heap.tables; + for (int i = 0; i < tables.Length; i++) { + var table = tables [i]; + if (table != null && table.Length > 0) + table_infos [i].Length = (uint)table.Length; + } + } + + byte GetHeapSizes () + { + byte heap_sizes = 0; + + if (metadata.string_heap.IsLarge) { + large_string = true; + heap_sizes |= 0x01; + } + + if (metadata.guid_heap.IsLarge) { + large_guid = true; + heap_sizes |= 0x02; + } + + if (metadata.blob_heap.IsLarge) { + large_blob = true; + heap_sizes |= 0x04; + } + + return heap_sizes; + } + + byte GetTableHeapVersion () + { + switch (module.Runtime) { + case TargetRuntime.Net_1_0: + case TargetRuntime.Net_1_1: + return 1; + default: + return 2; + } + } + + public void FixupData (RVA data_rva) + { + var table = GetTable (Table.FieldRVA); + if (table.length == 0) + return; + + var field_idx_size = GetTable (Table.Field).IsLarge ? 4 : 2; + var previous = this.position; + + base.position = table.position; + for (int i = 0; i < table.length; i++) { + var rva = ReadUInt32 (); + base.position -= 4; + WriteUInt32 (rva + data_rva); + base.position += field_idx_size; + } + + base.position = previous; + } + } + + sealed class ResourceBuffer : ByteBuffer { + + public ResourceBuffer () + : base (0) + { + } + + public uint AddResource (byte [] resource) + { + var offset = (uint)this.position; + WriteInt32 (resource.Length); + WriteBytes (resource); + return offset; + } + } + + sealed class DataBuffer : ByteBuffer { + + public DataBuffer () + : base (0) + { + } + + public RVA AddData (byte [] data) + { + var rva = (RVA)position; + WriteBytes (data); + return rva; + } + } + + abstract class HeapBuffer : ByteBuffer { + + public bool IsLarge { + get { return base.length > 65535; } + } + + public abstract bool IsEmpty { get; } + + protected HeapBuffer (int length) + : base (length) + { + } + } + + sealed class GuidHeapBuffer : HeapBuffer { + + readonly Dictionary guids = new Dictionary (); + + public override bool IsEmpty { + get { return length == 0; } + } + + public GuidHeapBuffer () + : base (16) + { + } + + public uint GetGuidIndex (Guid guid) + { + uint index; + if (guids.TryGetValue (guid, out index)) + return index; + + index = (uint)guids.Count + 1; + WriteGuid (guid); + guids.Add (guid, index); + return index; + } + + void WriteGuid (Guid guid) + { + WriteBytes (guid.ToByteArray ()); + } + } + + class StringHeapBuffer : HeapBuffer { + + protected Dictionary strings = new Dictionary (StringComparer.Ordinal); + + public sealed override bool IsEmpty { + get { return length <= 1; } + } + + public StringHeapBuffer () + : base (1) + { + WriteByte (0); + } + + public virtual uint GetStringIndex (string @string) + { + uint index; + if (strings.TryGetValue (@string, out index)) + return index; + + index = (uint)strings.Count + 1; + strings.Add (@string, index); + return index; + } + + public uint [] WriteStrings () + { + var sorted = SortStrings (strings); + strings = null; + + // Add 1 for empty string whose index and offset are both 0 + var string_offsets = new uint [sorted.Count + 1]; + string_offsets [0] = 0; + + // Find strings that can be folded + var previous = string.Empty; + foreach (var entry in sorted) { + var @string = entry.Key; + var index = entry.Value; + var position = base.position; + + if (previous.EndsWith (@string, StringComparison.Ordinal) && !IsLowSurrogateChar (entry.Key [0])) { + // Map over the tail of prev string. Watch for null-terminator of prev string. + string_offsets [index] = (uint)(position - (Encoding.UTF8.GetByteCount (entry.Key) + 1)); + } else { + string_offsets [index] = (uint)position; + WriteString (@string); + } + + previous = entry.Key; + } + + return string_offsets; + } + + static List> SortStrings (Dictionary strings) + { + var sorted = new List> (strings); + sorted.Sort (new SuffixSort ()); + return sorted; + } + + static bool IsLowSurrogateChar (int c) + { + return unchecked((uint)(c - 0xDC00)) <= 0xDFFF - 0xDC00; + } + + protected virtual void WriteString (string @string) + { + WriteBytes (Encoding.UTF8.GetBytes (@string)); + WriteByte (0); + } + + // Sorts strings such that a string is followed immediately by all strings + // that are a suffix of it. + private class SuffixSort : IComparer> { + + public int Compare (KeyValuePair xPair, KeyValuePair yPair) + { + var x = xPair.Key; + var y = yPair.Key; + + for (int i = x.Length - 1, j = y.Length - 1; i >= 0 & j >= 0; i--, j--) { + if (x [i] < y [j]) { + return -1; + } + + if (x [i] > y [j]) { + return +1; + } + } + + return y.Length.CompareTo (x.Length); + } + } + } + + sealed class BlobHeapBuffer : HeapBuffer { + + readonly Dictionary blobs = new Dictionary (new ByteBufferEqualityComparer ()); + + public override bool IsEmpty { + get { return length <= 1; } + } + + public BlobHeapBuffer () + : base (1) + { + WriteByte (0); + } + + public uint GetBlobIndex (ByteBuffer blob) + { + uint index; + if (blobs.TryGetValue (blob, out index)) + return index; + + index = (uint)base.position; + WriteBlob (blob); + blobs.Add (blob, index); + return index; + } + + void WriteBlob (ByteBuffer blob) + { + WriteCompressedUInt32 ((uint)blob.length); + WriteBytes (blob); + } + } + + sealed class UserStringHeapBuffer : StringHeapBuffer { + + public override uint GetStringIndex (string @string) + { + uint index; + if (strings.TryGetValue (@string, out index)) + return index; + + index = (uint)base.position; + WriteString (@string); + strings.Add (@string, index); + return index; + } + + protected override void WriteString (string @string) + { + WriteCompressedUInt32 ((uint)@string.Length * 2 + 1); + + byte special = 0; + + for (int i = 0; i < @string.Length; i++) { + var @char = @string [i]; + WriteUInt16 (@char); + + if (special == 1) + continue; + + if (@char < 0x20 || @char > 0x7e) { + if (@char > 0x7e + || (@char >= 0x01 && @char <= 0x08) + || (@char >= 0x0e && @char <= 0x1f) + || @char == 0x27 + || @char == 0x2d) { + + special = 1; + } + } + } + + WriteByte (special); + } + } + + sealed class PdbHeapBuffer : HeapBuffer { + + public override bool IsEmpty { + get { return false; } + } + + public PdbHeapBuffer () + : base (0) + { + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Buffers.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Buffers.cs.meta new file mode 100644 index 0000000..480046a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Buffers.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4b1d0a81b66e78341ab77acdd3b15234 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/CodedIndex.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/CodedIndex.cs new file mode 100644 index 0000000..601a347 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/CodedIndex.cs @@ -0,0 +1,29 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil.Metadata { + + enum CodedIndex { + TypeDefOrRef, + HasConstant, + HasCustomAttribute, + HasFieldMarshal, + HasDeclSecurity, + MemberRefParent, + HasSemantics, + MethodDefOrRef, + MemberForwarded, + Implementation, + CustomAttributeType, + ResolutionScope, + TypeOrMethodDef, + HasCustomDebugInformation, + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/CodedIndex.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/CodedIndex.cs.meta new file mode 100644 index 0000000..553ef4d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/CodedIndex.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dd50ffa8272dac444831e33b1b9d7f56 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/ElementType.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/ElementType.cs new file mode 100644 index 0000000..8351286 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/ElementType.cs @@ -0,0 +1,55 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil.Metadata { + + enum ElementType : byte { + None = 0x00, + Void = 0x01, + Boolean = 0x02, + Char = 0x03, + I1 = 0x04, + U1 = 0x05, + I2 = 0x06, + U2 = 0x07, + I4 = 0x08, + U4 = 0x09, + I8 = 0x0a, + U8 = 0x0b, + R4 = 0x0c, + R8 = 0x0d, + String = 0x0e, + Ptr = 0x0f, // Followed by token + ByRef = 0x10, // Followed by token + ValueType = 0x11, // Followed by token + Class = 0x12, // Followed by token + Var = 0x13, // Followed by generic parameter number + Array = 0x14, // + GenericInst = 0x15, // ... */ + TypedByRef = 0x16, + I = 0x18, // System.IntPtr + U = 0x19, // System.UIntPtr + FnPtr = 0x1b, // Followed by full method signature + Object = 0x1c, // System.Object + SzArray = 0x1d, // Single-dim array with 0 lower bound + MVar = 0x1e, // Followed by generic parameter number + CModReqD = 0x1f, // Required modifier : followed by a TypeDef or TypeRef token + CModOpt = 0x20, // Optional modifier : followed by a TypeDef or TypeRef token + Internal = 0x21, // Implemented within the CLI + Modifier = 0x40, // Or'd with following element types + Sentinel = 0x41, // Sentinel for varargs method signature + Pinned = 0x45, // Denotes a local variable that points at a pinned object + + // special undocumented constants + Type = 0x50, + Boxed = 0x51, + Enum = 0x55 + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/ElementType.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/ElementType.cs.meta new file mode 100644 index 0000000..58173f6 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/ElementType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 94c4cb498ef60034d83bb60d3f743832 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/GuidHeap.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/GuidHeap.cs new file mode 100644 index 0000000..ec9c644 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/GuidHeap.cs @@ -0,0 +1,36 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil.Metadata { + + sealed class GuidHeap : Heap { + + public GuidHeap (byte [] data) + : base (data) + { + } + + public Guid Read (uint index) + { + const int guid_size = 16; + + if (index == 0 || ((index - 1) + guid_size) > data.Length) + return new Guid (); + + var buffer = new byte [guid_size]; + + Buffer.BlockCopy (this.data, (int)((index - 1) * guid_size), buffer, 0, guid_size); + + return new Guid (buffer); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/GuidHeap.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/GuidHeap.cs.meta new file mode 100644 index 0000000..eaabe77 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/GuidHeap.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4bb39ab6484eb114b91a85d4b53f0f28 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Heap.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Heap.cs new file mode 100644 index 0000000..3cbb026 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Heap.cs @@ -0,0 +1,24 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil.Metadata { + + abstract class Heap { + + public int IndexSize; + + readonly internal byte [] data; + + protected Heap (byte [] data) + { + this.data = data; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Heap.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Heap.cs.meta new file mode 100644 index 0000000..c0d849b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Heap.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c343466f40d68574499155a2acd05e6b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/MetadataToken.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/MetadataToken.cs new file mode 100644 index 0000000..50b2556 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/MetadataToken.cs @@ -0,0 +1,94 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil { + + public struct MetadataToken : IEquatable { + + readonly uint token; + + public uint RID { + get { return token & 0x00ffffff; } + } + + public TokenType TokenType { + get { return (TokenType)(token & 0xff000000); } + } + + public static readonly MetadataToken Zero = new MetadataToken ((uint)0); + + public MetadataToken (uint token) + { + this.token = token; + } + + public MetadataToken (TokenType type) + : this (type, 0) + { + } + + public MetadataToken (TokenType type, uint rid) + { + token = (uint)type | rid; + } + + public MetadataToken (TokenType type, int rid) + { + token = (uint)type | (uint)rid; + } + + public int ToInt32 () + { + return (int)token; + } + + public uint ToUInt32 () + { + return token; + } + + public override int GetHashCode () + { + return (int)token; + } + + public bool Equals (MetadataToken other) + { + return other.token == token; + } + + public override bool Equals (object obj) + { + if (obj is MetadataToken) { + var other = (MetadataToken)obj; + return other.token == token; + } + + return false; + } + + public static bool operator == (MetadataToken one, MetadataToken other) + { + return one.token == other.token; + } + + public static bool operator != (MetadataToken one, MetadataToken other) + { + return one.token != other.token; + } + + public override string ToString () + { + return string.Format ("[{0}:0x{1}]", TokenType, RID.ToString ("x4")); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/MetadataToken.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/MetadataToken.cs.meta new file mode 100644 index 0000000..3026242 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/MetadataToken.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 755d1954236c374458be77d93493faf8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/PdbHeap.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/PdbHeap.cs new file mode 100644 index 0000000..6b56710 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/PdbHeap.cs @@ -0,0 +1,32 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using RID = System.UInt32; + +namespace MonoFN.Cecil.Metadata { + + sealed class PdbHeap : Heap { + + public byte [] Id; + public RID EntryPoint; + public long TypeSystemTables; + public uint [] TypeSystemTableRows; + + public PdbHeap (byte [] data) + : base (data) + { + } + + public bool HasTable (Table table) + { + return (TypeSystemTables & (1L << (int)table)) != 0; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/PdbHeap.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/PdbHeap.cs.meta new file mode 100644 index 0000000..6505525 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/PdbHeap.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c64dd6e20a2b11c44ad424d29380c14c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Row.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Row.cs new file mode 100644 index 0000000..880eeb3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Row.cs @@ -0,0 +1,152 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System.Collections.Generic; + +namespace MonoFN.Cecil.Metadata { + + struct Row { + internal T1 Col1; + internal T2 Col2; + + public Row (T1 col1, T2 col2) + { + Col1 = col1; + Col2 = col2; + } + } + + struct Row { + internal T1 Col1; + internal T2 Col2; + internal T3 Col3; + + public Row (T1 col1, T2 col2, T3 col3) + { + Col1 = col1; + Col2 = col2; + Col3 = col3; + } + } + + struct Row { + internal T1 Col1; + internal T2 Col2; + internal T3 Col3; + internal T4 Col4; + + public Row (T1 col1, T2 col2, T3 col3, T4 col4) + { + Col1 = col1; + Col2 = col2; + Col3 = col3; + Col4 = col4; + } + } + + struct Row { + internal T1 Col1; + internal T2 Col2; + internal T3 Col3; + internal T4 Col4; + internal T5 Col5; + + public Row (T1 col1, T2 col2, T3 col3, T4 col4, T5 col5) + { + Col1 = col1; + Col2 = col2; + Col3 = col3; + Col4 = col4; + Col5 = col5; + } + } + + struct Row { + internal T1 Col1; + internal T2 Col2; + internal T3 Col3; + internal T4 Col4; + internal T5 Col5; + internal T6 Col6; + + public Row (T1 col1, T2 col2, T3 col3, T4 col4, T5 col5, T6 col6) + { + Col1 = col1; + Col2 = col2; + Col3 = col3; + Col4 = col4; + Col5 = col5; + Col6 = col6; + } + } + + struct Row { + internal T1 Col1; + internal T2 Col2; + internal T3 Col3; + internal T4 Col4; + internal T5 Col5; + internal T6 Col6; + internal T7 Col7; + internal T8 Col8; + internal T9 Col9; + + public Row (T1 col1, T2 col2, T3 col3, T4 col4, T5 col5, T6 col6, T7 col7, T8 col8, T9 col9) + { + Col1 = col1; + Col2 = col2; + Col3 = col3; + Col4 = col4; + Col5 = col5; + Col6 = col6; + Col7 = col7; + Col8 = col8; + Col9 = col9; + } + } + + sealed class RowEqualityComparer : IEqualityComparer>, IEqualityComparer>, IEqualityComparer> { + + public bool Equals (Row x, Row y) + { + return x.Col1 == y.Col1 + && x.Col2 == y.Col2; + } + + public int GetHashCode (Row obj) + { + string x = obj.Col1, y = obj.Col2; + return (x != null ? x.GetHashCode () : 0) ^ (y != null ? y.GetHashCode () : 0); + } + + public bool Equals (Row x, Row y) + { + return x.Col1 == y.Col1 + && x.Col2 == y.Col2; + } + + public int GetHashCode (Row obj) + { + return (int)(obj.Col1 ^ obj.Col2); + } + + public bool Equals (Row x, Row y) + { + return x.Col1 == y.Col1 + && x.Col2 == y.Col2 + && x.Col3 == y.Col3; + } + + public int GetHashCode (Row obj) + { + return (int)(obj.Col1 ^ obj.Col2 ^ obj.Col3); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Row.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Row.cs.meta new file mode 100644 index 0000000..dc7d118 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Row.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 89fd184bcc2b97840a2c0dfeec671be2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/StringHeap.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/StringHeap.cs new file mode 100644 index 0000000..22559df --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/StringHeap.cs @@ -0,0 +1,59 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System.Collections.Generic; +using System.Text; + +namespace MonoFN.Cecil.Metadata { + + class StringHeap : Heap { + + readonly Dictionary strings = new Dictionary (); + + public StringHeap (byte [] data) + : base (data) + { + } + + public string Read (uint index) + { + if (index == 0) + return string.Empty; + + string @string; + if (strings.TryGetValue (index, out @string)) + return @string; + + if (index > data.Length - 1) + return string.Empty; + + @string = ReadStringAt (index); + if (@string.Length != 0) + strings.Add (index, @string); + + return @string; + } + + protected virtual string ReadStringAt (uint index) + { + int length = 0; + int start = (int)index; + + for (int i = start; ; i++) { + if (data [i] == 0) + break; + + length++; + } + + return Encoding.UTF8.GetString (data, start, length); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/StringHeap.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/StringHeap.cs.meta new file mode 100644 index 0000000..9e51e42 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/StringHeap.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e0fc64863ca825b4e994640b0be9a938 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/TableHeap.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/TableHeap.cs new file mode 100644 index 0000000..e4f206b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/TableHeap.cs @@ -0,0 +1,101 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil.Metadata { + + enum Table : byte { + Module = 0x00, + TypeRef = 0x01, + TypeDef = 0x02, + FieldPtr = 0x03, + Field = 0x04, + MethodPtr = 0x05, + Method = 0x06, + ParamPtr = 0x07, + Param = 0x08, + InterfaceImpl = 0x09, + MemberRef = 0x0a, + Constant = 0x0b, + CustomAttribute = 0x0c, + FieldMarshal = 0x0d, + DeclSecurity = 0x0e, + ClassLayout = 0x0f, + FieldLayout = 0x10, + StandAloneSig = 0x11, + EventMap = 0x12, + EventPtr = 0x13, + Event = 0x14, + PropertyMap = 0x15, + PropertyPtr = 0x16, + Property = 0x17, + MethodSemantics = 0x18, + MethodImpl = 0x19, + ModuleRef = 0x1a, + TypeSpec = 0x1b, + ImplMap = 0x1c, + FieldRVA = 0x1d, + EncLog = 0x1e, + EncMap = 0x1f, + Assembly = 0x20, + AssemblyProcessor = 0x21, + AssemblyOS = 0x22, + AssemblyRef = 0x23, + AssemblyRefProcessor = 0x24, + AssemblyRefOS = 0x25, + File = 0x26, + ExportedType = 0x27, + ManifestResource = 0x28, + NestedClass = 0x29, + GenericParam = 0x2a, + MethodSpec = 0x2b, + GenericParamConstraint = 0x2c, + + Document = 0x30, + MethodDebugInformation = 0x31, + LocalScope = 0x32, + LocalVariable = 0x33, + LocalConstant = 0x34, + ImportScope = 0x35, + StateMachineMethod = 0x36, + CustomDebugInformation = 0x37, + } + + struct TableInformation { + public uint Offset; + public uint Length; + public uint RowSize; + + public bool IsLarge { + get { return Length > ushort.MaxValue; } + } + } + + sealed class TableHeap : Heap { + + public long Valid; + public long Sorted; + + public readonly TableInformation [] Tables = new TableInformation [Mixin.TableCount]; + + public TableInformation this [Table table] { + get { return Tables [(int)table]; } + } + + public TableHeap (byte [] data) + : base (data) + { + } + + public bool HasTable (Table table) + { + return (Valid & (1L << (int)table)) != 0; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/TableHeap.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/TableHeap.cs.meta new file mode 100644 index 0000000..64fe6ce --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/TableHeap.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 978b1609b2ddc0c4baf628cc608456a9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/TokenType.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/TokenType.cs new file mode 100644 index 0000000..e527181 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/TokenType.cs @@ -0,0 +1,49 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil { + + public enum TokenType : uint { + Module = 0x00000000, + TypeRef = 0x01000000, + TypeDef = 0x02000000, + Field = 0x04000000, + Method = 0x06000000, + Param = 0x08000000, + InterfaceImpl = 0x09000000, + MemberRef = 0x0a000000, + CustomAttribute = 0x0c000000, + Permission = 0x0e000000, + Signature = 0x11000000, + Event = 0x14000000, + Property = 0x17000000, + ModuleRef = 0x1a000000, + TypeSpec = 0x1b000000, + Assembly = 0x20000000, + AssemblyRef = 0x23000000, + File = 0x26000000, + ExportedType = 0x27000000, + ManifestResource = 0x28000000, + GenericParam = 0x2a000000, + MethodSpec = 0x2b000000, + GenericParamConstraint = 0x2c000000, + + Document = 0x30000000, + MethodDebugInformation = 0x31000000, + LocalScope = 0x32000000, + LocalVariable = 0x33000000, + LocalConstant = 0x34000000, + ImportScope = 0x35000000, + StateMachineMethod = 0x36000000, + CustomDebugInformation = 0x37000000, + + String = 0x70000000, + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/TokenType.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/TokenType.cs.meta new file mode 100644 index 0000000..7dd9eb4 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/TokenType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dba0af6138c6a084b8787e88b20b142a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/UserStringHeap.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/UserStringHeap.cs new file mode 100644 index 0000000..afdaa2c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/UserStringHeap.cs @@ -0,0 +1,36 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil.Metadata { + + sealed class UserStringHeap : StringHeap { + + public UserStringHeap (byte [] data) + : base (data) + { + } + + protected override string ReadStringAt (uint index) + { + int start = (int)index; + + uint length = (uint)(data.ReadCompressedUInt32 (ref start) & ~1); + if (length < 1) + return string.Empty; + + var chars = new char [length / 2]; + + for (int i = start, j = 0; i < start + length; i += 2) + chars [j++] = (char)(data [i] | (data [i + 1] << 8)); + + return new string (chars); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/UserStringHeap.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/UserStringHeap.cs.meta new file mode 100644 index 0000000..40594bf --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/UserStringHeap.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2d89100b382ef8e4ea67917917d04db6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Utilities.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Utilities.cs new file mode 100644 index 0000000..c5a3b03 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Utilities.cs @@ -0,0 +1,649 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.Metadata; +using System; + +namespace MonoFN.Cecil { + + static partial class Mixin { + + public const int TableCount = 58; + public const int CodedIndexCount = 14; + + public static uint ReadCompressedUInt32 (this byte [] data, ref int position) + { + uint integer; + if ((data [position] & 0x80) == 0) { + integer = data [position]; + position++; + } else if ((data [position] & 0x40) == 0) { + integer = (uint)(data [position] & ~0x80) << 8; + integer |= data [position + 1]; + position += 2; + } else { + integer = (uint)(data [position] & ~0xc0) << 24; + integer |= (uint)data [position + 1] << 16; + integer |= (uint)data [position + 2] << 8; + integer |= (uint)data [position + 3]; + position += 4; + } + return integer; + } + + public static MetadataToken GetMetadataToken (this CodedIndex self, uint data) + { + uint rid; + TokenType token_type; + switch (self) { + case CodedIndex.TypeDefOrRef: + rid = data >> 2; + switch (data & 3) { + case 0: + token_type = TokenType.TypeDef; goto ret; + case 1: + token_type = TokenType.TypeRef; goto ret; + case 2: + token_type = TokenType.TypeSpec; goto ret; + default: + goto exit; + } + case CodedIndex.HasConstant: + rid = data >> 2; + switch (data & 3) { + case 0: + token_type = TokenType.Field; goto ret; + case 1: + token_type = TokenType.Param; goto ret; + case 2: + token_type = TokenType.Property; goto ret; + default: + goto exit; + } + case CodedIndex.HasCustomAttribute: + rid = data >> 5; + switch (data & 31) { + case 0: + token_type = TokenType.Method; goto ret; + case 1: + token_type = TokenType.Field; goto ret; + case 2: + token_type = TokenType.TypeRef; goto ret; + case 3: + token_type = TokenType.TypeDef; goto ret; + case 4: + token_type = TokenType.Param; goto ret; + case 5: + token_type = TokenType.InterfaceImpl; goto ret; + case 6: + token_type = TokenType.MemberRef; goto ret; + case 7: + token_type = TokenType.Module; goto ret; + case 8: + token_type = TokenType.Permission; goto ret; + case 9: + token_type = TokenType.Property; goto ret; + case 10: + token_type = TokenType.Event; goto ret; + case 11: + token_type = TokenType.Signature; goto ret; + case 12: + token_type = TokenType.ModuleRef; goto ret; + case 13: + token_type = TokenType.TypeSpec; goto ret; + case 14: + token_type = TokenType.Assembly; goto ret; + case 15: + token_type = TokenType.AssemblyRef; goto ret; + case 16: + token_type = TokenType.File; goto ret; + case 17: + token_type = TokenType.ExportedType; goto ret; + case 18: + token_type = TokenType.ManifestResource; goto ret; + case 19: + token_type = TokenType.GenericParam; goto ret; + case 20: + token_type = TokenType.GenericParamConstraint; goto ret; + case 21: + token_type = TokenType.MethodSpec; goto ret; + default: + goto exit; + } + case CodedIndex.HasFieldMarshal: + rid = data >> 1; + switch (data & 1) { + case 0: + token_type = TokenType.Field; goto ret; + case 1: + token_type = TokenType.Param; goto ret; + default: + goto exit; + } + case CodedIndex.HasDeclSecurity: + rid = data >> 2; + switch (data & 3) { + case 0: + token_type = TokenType.TypeDef; goto ret; + case 1: + token_type = TokenType.Method; goto ret; + case 2: + token_type = TokenType.Assembly; goto ret; + default: + goto exit; + } + case CodedIndex.MemberRefParent: + rid = data >> 3; + switch (data & 7) { + case 0: + token_type = TokenType.TypeDef; goto ret; + case 1: + token_type = TokenType.TypeRef; goto ret; + case 2: + token_type = TokenType.ModuleRef; goto ret; + case 3: + token_type = TokenType.Method; goto ret; + case 4: + token_type = TokenType.TypeSpec; goto ret; + default: + goto exit; + } + case CodedIndex.HasSemantics: + rid = data >> 1; + switch (data & 1) { + case 0: + token_type = TokenType.Event; goto ret; + case 1: + token_type = TokenType.Property; goto ret; + default: + goto exit; + } + case CodedIndex.MethodDefOrRef: + rid = data >> 1; + switch (data & 1) { + case 0: + token_type = TokenType.Method; goto ret; + case 1: + token_type = TokenType.MemberRef; goto ret; + default: + goto exit; + } + case CodedIndex.MemberForwarded: + rid = data >> 1; + switch (data & 1) { + case 0: + token_type = TokenType.Field; goto ret; + case 1: + token_type = TokenType.Method; goto ret; + default: + goto exit; + } + case CodedIndex.Implementation: + rid = data >> 2; + switch (data & 3) { + case 0: + token_type = TokenType.File; goto ret; + case 1: + token_type = TokenType.AssemblyRef; goto ret; + case 2: + token_type = TokenType.ExportedType; goto ret; + default: + goto exit; + } + case CodedIndex.CustomAttributeType: + rid = data >> 3; + switch (data & 7) { + case 2: + token_type = TokenType.Method; goto ret; + case 3: + token_type = TokenType.MemberRef; goto ret; + default: + goto exit; + } + case CodedIndex.ResolutionScope: + rid = data >> 2; + switch (data & 3) { + case 0: + token_type = TokenType.Module; goto ret; + case 1: + token_type = TokenType.ModuleRef; goto ret; + case 2: + token_type = TokenType.AssemblyRef; goto ret; + case 3: + token_type = TokenType.TypeRef; goto ret; + default: + goto exit; + } + case CodedIndex.TypeOrMethodDef: + rid = data >> 1; + switch (data & 1) { + case 0: + token_type = TokenType.TypeDef; goto ret; + case 1: + token_type = TokenType.Method; goto ret; + default: goto exit; + } + case CodedIndex.HasCustomDebugInformation: + rid = data >> 5; + switch (data & 31) { + case 0: + token_type = TokenType.Method; goto ret; + case 1: + token_type = TokenType.Field; goto ret; + case 2: + token_type = TokenType.TypeRef; goto ret; + case 3: + token_type = TokenType.TypeDef; goto ret; + case 4: + token_type = TokenType.Param; goto ret; + case 5: + token_type = TokenType.InterfaceImpl; goto ret; + case 6: + token_type = TokenType.MemberRef; goto ret; + case 7: + token_type = TokenType.Module; goto ret; + case 8: + token_type = TokenType.Permission; goto ret; + case 9: + token_type = TokenType.Property; goto ret; + case 10: + token_type = TokenType.Event; goto ret; + case 11: + token_type = TokenType.Signature; goto ret; + case 12: + token_type = TokenType.ModuleRef; goto ret; + case 13: + token_type = TokenType.TypeSpec; goto ret; + case 14: + token_type = TokenType.Assembly; goto ret; + case 15: + token_type = TokenType.AssemblyRef; goto ret; + case 16: + token_type = TokenType.File; goto ret; + case 17: + token_type = TokenType.ExportedType; goto ret; + case 18: + token_type = TokenType.ManifestResource; goto ret; + case 19: + token_type = TokenType.GenericParam; goto ret; + case 20: + token_type = TokenType.GenericParamConstraint; goto ret; + case 21: + token_type = TokenType.MethodSpec; goto ret; + case 22: + token_type = TokenType.Document; goto ret; + case 23: + token_type = TokenType.LocalScope; goto ret; + case 24: + token_type = TokenType.LocalVariable; goto ret; + case 25: + token_type = TokenType.LocalConstant; goto ret; + case 26: + token_type = TokenType.ImportScope; goto ret; + default: + goto exit; + } + default: + goto exit; + } + ret: + return new MetadataToken (token_type, rid); + exit: + return MetadataToken.Zero; + } + + public static uint CompressMetadataToken (this CodedIndex self, MetadataToken token) + { + uint ret = 0; + if (token.RID == 0) + return ret; + switch (self) { + case CodedIndex.TypeDefOrRef: + ret = token.RID << 2; + switch (token.TokenType) { + case TokenType.TypeDef: + return ret | 0; + case TokenType.TypeRef: + return ret | 1; + case TokenType.TypeSpec: + return ret | 2; + default: + goto exit; + } + case CodedIndex.HasConstant: + ret = token.RID << 2; + switch (token.TokenType) { + case TokenType.Field: + return ret | 0; + case TokenType.Param: + return ret | 1; + case TokenType.Property: + return ret | 2; + default: + goto exit; + } + case CodedIndex.HasCustomAttribute: + ret = token.RID << 5; + switch (token.TokenType) { + case TokenType.Method: + return ret | 0; + case TokenType.Field: + return ret | 1; + case TokenType.TypeRef: + return ret | 2; + case TokenType.TypeDef: + return ret | 3; + case TokenType.Param: + return ret | 4; + case TokenType.InterfaceImpl: + return ret | 5; + case TokenType.MemberRef: + return ret | 6; + case TokenType.Module: + return ret | 7; + case TokenType.Permission: + return ret | 8; + case TokenType.Property: + return ret | 9; + case TokenType.Event: + return ret | 10; + case TokenType.Signature: + return ret | 11; + case TokenType.ModuleRef: + return ret | 12; + case TokenType.TypeSpec: + return ret | 13; + case TokenType.Assembly: + return ret | 14; + case TokenType.AssemblyRef: + return ret | 15; + case TokenType.File: + return ret | 16; + case TokenType.ExportedType: + return ret | 17; + case TokenType.ManifestResource: + return ret | 18; + case TokenType.GenericParam: + return ret | 19; + case TokenType.GenericParamConstraint: + return ret | 20; + case TokenType.MethodSpec: + return ret | 21; + default: + goto exit; + } + case CodedIndex.HasFieldMarshal: + ret = token.RID << 1; + switch (token.TokenType) { + case TokenType.Field: + return ret | 0; + case TokenType.Param: + return ret | 1; + default: + goto exit; + } + case CodedIndex.HasDeclSecurity: + ret = token.RID << 2; + switch (token.TokenType) { + case TokenType.TypeDef: + return ret | 0; + case TokenType.Method: + return ret | 1; + case TokenType.Assembly: + return ret | 2; + default: + goto exit; + } + case CodedIndex.MemberRefParent: + ret = token.RID << 3; + switch (token.TokenType) { + case TokenType.TypeDef: + return ret | 0; + case TokenType.TypeRef: + return ret | 1; + case TokenType.ModuleRef: + return ret | 2; + case TokenType.Method: + return ret | 3; + case TokenType.TypeSpec: + return ret | 4; + default: + goto exit; + } + case CodedIndex.HasSemantics: + ret = token.RID << 1; + switch (token.TokenType) { + case TokenType.Event: + return ret | 0; + case TokenType.Property: + return ret | 1; + default: + goto exit; + } + case CodedIndex.MethodDefOrRef: + ret = token.RID << 1; + switch (token.TokenType) { + case TokenType.Method: + return ret | 0; + case TokenType.MemberRef: + return ret | 1; + default: + goto exit; + } + case CodedIndex.MemberForwarded: + ret = token.RID << 1; + switch (token.TokenType) { + case TokenType.Field: + return ret | 0; + case TokenType.Method: + return ret | 1; + default: + goto exit; + } + case CodedIndex.Implementation: + ret = token.RID << 2; + switch (token.TokenType) { + case TokenType.File: + return ret | 0; + case TokenType.AssemblyRef: + return ret | 1; + case TokenType.ExportedType: + return ret | 2; + default: + goto exit; + } + case CodedIndex.CustomAttributeType: + ret = token.RID << 3; + switch (token.TokenType) { + case TokenType.Method: + return ret | 2; + case TokenType.MemberRef: + return ret | 3; + default: + goto exit; + } + case CodedIndex.ResolutionScope: + ret = token.RID << 2; + switch (token.TokenType) { + case TokenType.Module: + return ret | 0; + case TokenType.ModuleRef: + return ret | 1; + case TokenType.AssemblyRef: + return ret | 2; + case TokenType.TypeRef: + return ret | 3; + default: + goto exit; + } + case CodedIndex.TypeOrMethodDef: + ret = token.RID << 1; + switch (token.TokenType) { + case TokenType.TypeDef: + return ret | 0; + case TokenType.Method: + return ret | 1; + default: + goto exit; + } + case CodedIndex.HasCustomDebugInformation: + ret = token.RID << 5; + switch (token.TokenType) { + case TokenType.Method: + return ret | 0; + case TokenType.Field: + return ret | 1; + case TokenType.TypeRef: + return ret | 2; + case TokenType.TypeDef: + return ret | 3; + case TokenType.Param: + return ret | 4; + case TokenType.InterfaceImpl: + return ret | 5; + case TokenType.MemberRef: + return ret | 6; + case TokenType.Module: + return ret | 7; + case TokenType.Permission: + return ret | 8; + case TokenType.Property: + return ret | 9; + case TokenType.Event: + return ret | 10; + case TokenType.Signature: + return ret | 11; + case TokenType.ModuleRef: + return ret | 12; + case TokenType.TypeSpec: + return ret | 13; + case TokenType.Assembly: + return ret | 14; + case TokenType.AssemblyRef: + return ret | 15; + case TokenType.File: + return ret | 16; + case TokenType.ExportedType: + return ret | 17; + case TokenType.ManifestResource: + return ret | 18; + case TokenType.GenericParam: + return ret | 19; + case TokenType.GenericParamConstraint: + return ret | 20; + case TokenType.MethodSpec: + return ret | 21; + case TokenType.Document: + return ret | 22; + case TokenType.LocalScope: + return ret | 23; + case TokenType.LocalVariable: + return ret | 24; + case TokenType.LocalConstant: + return ret | 25; + case TokenType.ImportScope: + return ret | 26; + default: + goto exit; + } + default: + goto exit; + } + exit: + throw new ArgumentException (); + } + + public static int GetSize (this CodedIndex self, Func counter) + { + int bits; + Table [] tables; + + switch (self) { + case CodedIndex.TypeDefOrRef: + bits = 2; + tables = new [] { Table.TypeDef, Table.TypeRef, Table.TypeSpec }; + break; + case CodedIndex.HasConstant: + bits = 2; + tables = new [] { Table.Field, Table.Param, Table.Property }; + break; + case CodedIndex.HasCustomAttribute: + bits = 5; + tables = new [] { + Table.Method, Table.Field, Table.TypeRef, Table.TypeDef, Table.Param, Table.InterfaceImpl, Table.MemberRef, + Table.Module, Table.DeclSecurity, Table.Property, Table.Event, Table.StandAloneSig, Table.ModuleRef, + Table.TypeSpec, Table.Assembly, Table.AssemblyRef, Table.File, Table.ExportedType, + Table.ManifestResource, Table.GenericParam, Table.GenericParamConstraint, Table.MethodSpec, + }; + break; + case CodedIndex.HasFieldMarshal: + bits = 1; + tables = new [] { Table.Field, Table.Param }; + break; + case CodedIndex.HasDeclSecurity: + bits = 2; + tables = new [] { Table.TypeDef, Table.Method, Table.Assembly }; + break; + case CodedIndex.MemberRefParent: + bits = 3; + tables = new [] { Table.TypeDef, Table.TypeRef, Table.ModuleRef, Table.Method, Table.TypeSpec }; + break; + case CodedIndex.HasSemantics: + bits = 1; + tables = new [] { Table.Event, Table.Property }; + break; + case CodedIndex.MethodDefOrRef: + bits = 1; + tables = new [] { Table.Method, Table.MemberRef }; + break; + case CodedIndex.MemberForwarded: + bits = 1; + tables = new [] { Table.Field, Table.Method }; + break; + case CodedIndex.Implementation: + bits = 2; + tables = new [] { Table.File, Table.AssemblyRef, Table.ExportedType }; + break; + case CodedIndex.CustomAttributeType: + bits = 3; + tables = new [] { Table.Method, Table.MemberRef }; + break; + case CodedIndex.ResolutionScope: + bits = 2; + tables = new [] { Table.Module, Table.ModuleRef, Table.AssemblyRef, Table.TypeRef }; + break; + case CodedIndex.TypeOrMethodDef: + bits = 1; + tables = new [] { Table.TypeDef, Table.Method }; + break; + case CodedIndex.HasCustomDebugInformation: + bits = 5; + tables = new [] { + Table.Method, Table.Field, Table.TypeRef, Table.TypeDef, Table.Param, Table.InterfaceImpl, Table.MemberRef, + Table.Module, Table.DeclSecurity, Table.Property, Table.Event, Table.StandAloneSig, Table.ModuleRef, + Table.TypeSpec, Table.Assembly, Table.AssemblyRef, Table.File, Table.ExportedType, + Table.ManifestResource, Table.GenericParam, Table.GenericParamConstraint, Table.MethodSpec, + Table.Document, Table.LocalScope, Table.LocalVariable, Table.LocalConstant, Table.ImportScope, + }; + break; + default: + throw new ArgumentException (); + } + + int max = 0; + + for (int i = 0; i < tables.Length; i++) { + max = System.Math.Max (counter (tables [i]), max); + } + + return max < (1 << (16 - bits)) ? 2 : 4; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Utilities.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Utilities.cs.meta new file mode 100644 index 0000000..6fab64b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Utilities.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4785957c0c546de4680e1196a57f66d2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE.meta new file mode 100644 index 0000000..9797d0a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8c654da31387b0a4ea2a1128a28ef87d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/BinaryStreamReader.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/BinaryStreamReader.cs new file mode 100644 index 0000000..c584ff8 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/BinaryStreamReader.cs @@ -0,0 +1,53 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System.IO; + +namespace MonoFN.Cecil.PE { + + class BinaryStreamReader : BinaryReader { + + public int Position { + get { return (int)BaseStream.Position; } + set { BaseStream.Position = value; } + } + + public int Length { + get { return (int)BaseStream.Length; } + } + + public BinaryStreamReader (Stream stream) + : base (stream) + { + } + + public void Advance (int bytes) + { + BaseStream.Seek (bytes, SeekOrigin.Current); + } + + public void MoveTo (uint position) + { + BaseStream.Seek (position, SeekOrigin.Begin); + } + + public void Align (int align) + { + align--; + var position = Position; + Advance (((position + align) & ~align) - position); + } + + public DataDirectory ReadDataDirectory () + { + return new DataDirectory (ReadUInt32 (), ReadUInt32 ()); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/BinaryStreamReader.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/BinaryStreamReader.cs.meta new file mode 100644 index 0000000..a1d4c1d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/BinaryStreamReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7492ed3a048237443b99d7a25e806ce6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/BinaryStreamWriter.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/BinaryStreamWriter.cs new file mode 100644 index 0000000..34ad11a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/BinaryStreamWriter.cs @@ -0,0 +1,88 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System.IO; + +namespace MonoFN.Cecil.PE { + + class BinaryStreamWriter : BinaryWriter { + + public int Position { + get { return (int)BaseStream.Position; } + set { BaseStream.Position = value; } + } + + public BinaryStreamWriter (Stream stream) + : base (stream) + { + } + + public void WriteByte (byte value) + { + Write (value); + } + + public void WriteUInt16 (ushort value) + { + Write (value); + } + + public void WriteInt16 (short value) + { + Write (value); + } + + public void WriteUInt32 (uint value) + { + Write (value); + } + + public void WriteInt32 (int value) + { + Write (value); + } + + public void WriteUInt64 (ulong value) + { + Write (value); + } + + public void WriteBytes (byte [] bytes) + { + Write (bytes); + } + + public void WriteDataDirectory (DataDirectory directory) + { + Write (directory.VirtualAddress); + Write (directory.Size); + } + + public void WriteBuffer (ByteBuffer buffer) + { + Write (buffer.buffer, 0, buffer.length); + } + + protected void Advance (int bytes) + { + BaseStream.Seek (bytes, SeekOrigin.Current); + } + + public void Align (int align) + { + align--; + var position = Position; + var bytes = ((position + align) & ~align) - position; + + for (int i = 0; i < bytes; i++) + WriteByte (0); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/BinaryStreamWriter.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/BinaryStreamWriter.cs.meta new file mode 100644 index 0000000..2c0e298 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/BinaryStreamWriter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 26d384e0dd0e44549a9faf09adbd0a41 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ByteBuffer.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ByteBuffer.cs new file mode 100644 index 0000000..8e5c947 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ByteBuffer.cs @@ -0,0 +1,335 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil.PE { + + class ByteBuffer { + + internal byte [] buffer; + internal int length; + internal int position; + + public ByteBuffer () + { + this.buffer = Empty.Array; + } + + public ByteBuffer (int length) + { + this.buffer = new byte [length]; + } + + public ByteBuffer (byte [] buffer) + { + this.buffer = buffer ?? Empty.Array; + this.length = this.buffer.Length; + } + + public void Advance (int length) + { + position += length; + } + + public byte ReadByte () + { + return buffer [position++]; + } + + public sbyte ReadSByte () + { + return (sbyte)ReadByte (); + } + + public byte [] ReadBytes (int length) + { + var bytes = new byte [length]; + Buffer.BlockCopy (buffer, position, bytes, 0, length); + position += length; + return bytes; + } + + public ushort ReadUInt16 () + { + ushort value = (ushort)(buffer [position] + | (buffer [position + 1] << 8)); + position += 2; + return value; + } + + public short ReadInt16 () + { + return (short)ReadUInt16 (); + } + + public uint ReadUInt32 () + { + uint value = (uint)(buffer [position] + | (buffer [position + 1] << 8) + | (buffer [position + 2] << 16) + | (buffer [position + 3] << 24)); + position += 4; + return value; + } + + public int ReadInt32 () + { + return (int)ReadUInt32 (); + } + + public ulong ReadUInt64 () + { + uint low = ReadUInt32 (); + uint high = ReadUInt32 (); + + return (((ulong)high) << 32) | low; + } + + public long ReadInt64 () + { + return (long)ReadUInt64 (); + } + + public uint ReadCompressedUInt32 () + { + byte first = ReadByte (); + if ((first & 0x80) == 0) + return first; + + if ((first & 0x40) == 0) + return ((uint)(first & ~0x80) << 8) + | ReadByte (); + + return ((uint)(first & ~0xc0) << 24) + | (uint)ReadByte () << 16 + | (uint)ReadByte () << 8 + | ReadByte (); + } + + public int ReadCompressedInt32 () + { + var b = buffer [position]; + var u = (int)ReadCompressedUInt32 (); + var v = u >> 1; + if ((u & 1) == 0) + return v; + + switch (b & 0xc0) { + case 0: + case 0x40: + return v - 0x40; + case 0x80: + return v - 0x2000; + default: + return v - 0x10000000; + } + } + + public float ReadSingle () + { + if (!BitConverter.IsLittleEndian) { + var bytes = ReadBytes (4); + Array.Reverse (bytes); + return BitConverter.ToSingle (bytes, 0); + } + + float value = BitConverter.ToSingle (buffer, position); + position += 4; + return value; + } + + public double ReadDouble () + { + if (!BitConverter.IsLittleEndian) { + var bytes = ReadBytes (8); + Array.Reverse (bytes); + return BitConverter.ToDouble (bytes, 0); + } + + double value = BitConverter.ToDouble (buffer, position); + position += 8; + return value; + } + + public void WriteByte (byte value) + { + if (position == buffer.Length) + Grow (1); + + buffer [position++] = value; + + if (position > length) + length = position; + } + + public void WriteSByte (sbyte value) + { + WriteByte ((byte)value); + } + + public void WriteUInt16 (ushort value) + { + if (position + 2 > buffer.Length) + Grow (2); + + buffer [position++] = (byte)value; + buffer [position++] = (byte)(value >> 8); + + if (position > length) + length = position; + } + + public void WriteInt16 (short value) + { + WriteUInt16 ((ushort)value); + } + + public void WriteUInt32 (uint value) + { + if (position + 4 > buffer.Length) + Grow (4); + + buffer [position++] = (byte)value; + buffer [position++] = (byte)(value >> 8); + buffer [position++] = (byte)(value >> 16); + buffer [position++] = (byte)(value >> 24); + + if (position > length) + length = position; + } + + public void WriteInt32 (int value) + { + WriteUInt32 ((uint)value); + } + + public void WriteUInt64 (ulong value) + { + if (position + 8 > buffer.Length) + Grow (8); + + buffer [position++] = (byte)value; + buffer [position++] = (byte)(value >> 8); + buffer [position++] = (byte)(value >> 16); + buffer [position++] = (byte)(value >> 24); + buffer [position++] = (byte)(value >> 32); + buffer [position++] = (byte)(value >> 40); + buffer [position++] = (byte)(value >> 48); + buffer [position++] = (byte)(value >> 56); + + if (position > length) + length = position; + } + + public void WriteInt64 (long value) + { + WriteUInt64 ((ulong)value); + } + + public void WriteCompressedUInt32 (uint value) + { + if (value < 0x80) + WriteByte ((byte)value); + else if (value < 0x4000) { + WriteByte ((byte)(0x80 | (value >> 8))); + WriteByte ((byte)(value & 0xff)); + } else { + WriteByte ((byte)((value >> 24) | 0xc0)); + WriteByte ((byte)((value >> 16) & 0xff)); + WriteByte ((byte)((value >> 8) & 0xff)); + WriteByte ((byte)(value & 0xff)); + } + } + + public void WriteCompressedInt32 (int value) + { + if (value >= 0) { + WriteCompressedUInt32 ((uint)(value << 1)); + return; + } + + if (value > -0x40) + value = 0x40 + value; + else if (value >= -0x2000) + value = 0x2000 + value; + else if (value >= -0x20000000) + value = 0x20000000 + value; + + WriteCompressedUInt32 ((uint)((value << 1) | 1)); + } + + public void WriteBytes (byte [] bytes) + { + var length = bytes.Length; + if (position + length > buffer.Length) + Grow (length); + + Buffer.BlockCopy (bytes, 0, buffer, position, length); + position += length; + + if (position > this.length) + this.length = position; + } + + public void WriteBytes (int length) + { + if (position + length > buffer.Length) + Grow (length); + + position += length; + + if (position > this.length) + this.length = position; + } + + public void WriteBytes (ByteBuffer buffer) + { + if (position + buffer.length > this.buffer.Length) + Grow (buffer.length); + + Buffer.BlockCopy (buffer.buffer, 0, this.buffer, position, buffer.length); + position += buffer.length; + + if (position > this.length) + this.length = position; + } + + public void WriteSingle (float value) + { + var bytes = BitConverter.GetBytes (value); + + if (!BitConverter.IsLittleEndian) + Array.Reverse (bytes); + + WriteBytes (bytes); + } + + public void WriteDouble (double value) + { + var bytes = BitConverter.GetBytes (value); + + if (!BitConverter.IsLittleEndian) + Array.Reverse (bytes); + + WriteBytes (bytes); + } + + void Grow (int desired) + { + var current = this.buffer; + var current_length = current.Length; + + var buffer = new byte [System.Math.Max (current_length + desired, current_length * 2)]; + Buffer.BlockCopy (current, 0, buffer, 0, current_length); + this.buffer = buffer; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ByteBuffer.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ByteBuffer.cs.meta new file mode 100644 index 0000000..15415a1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ByteBuffer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3752816249ea16e4aba70adb03f01673 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ByteBufferEqualityComparer.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ByteBufferEqualityComparer.cs new file mode 100644 index 0000000..25caec1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ByteBufferEqualityComparer.cs @@ -0,0 +1,47 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System.Collections.Generic; + +namespace MonoFN.Cecil.PE { + + sealed class ByteBufferEqualityComparer : IEqualityComparer { + + public bool Equals (ByteBuffer x, ByteBuffer y) + { + if (x.length != y.length) + return false; + + var x_buffer = x.buffer; + var y_buffer = y.buffer; + + for (int i = 0; i < x.length; i++) + if (x_buffer [i] != y_buffer [i]) + return false; + + return true; + } + + public int GetHashCode (ByteBuffer buffer) + { + // See http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function + const int fnv_offset_bias = unchecked((int)2166136261); + const int fnv_prime = 16777619; + + var hash_code = fnv_offset_bias; + var bytes = buffer.buffer; + + for (int i = 0; i < buffer.length; i++) + hash_code = unchecked((hash_code ^ bytes [i]) * fnv_prime); + + return hash_code; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ByteBufferEqualityComparer.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ByteBufferEqualityComparer.cs.meta new file mode 100644 index 0000000..402b21d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ByteBufferEqualityComparer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0db5f2f7f9a349d4d89e2329ffd563b2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/DataDirectory.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/DataDirectory.cs new file mode 100644 index 0000000..81121af --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/DataDirectory.cs @@ -0,0 +1,30 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using RVA = System.UInt32; + +namespace MonoFN.Cecil.PE { + + struct DataDirectory { + + public readonly RVA VirtualAddress; + public readonly uint Size; + + public bool IsZero { + get { return VirtualAddress == 0 && Size == 0; } + } + + public DataDirectory (RVA rva, uint size) + { + this.VirtualAddress = rva; + this.Size = size; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/DataDirectory.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/DataDirectory.cs.meta new file mode 100644 index 0000000..b196baa --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/DataDirectory.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7cd272a7ff953734bbacab398dfc9fe8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/Image.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/Image.cs new file mode 100644 index 0000000..a129178 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/Image.cs @@ -0,0 +1,169 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.Cil; +using MonoFN.Cecil.Metadata; +using System; +using System.IO; +using RVA = System.UInt32; + +namespace MonoFN.Cecil.PE { + + sealed class Image : IDisposable { + + public Disposable Stream; + public string FileName; + + public ModuleKind Kind; + public uint Characteristics; + public string RuntimeVersion; + public TargetArchitecture Architecture; + public ModuleCharacteristics DllCharacteristics; + public ushort LinkerVersion; + public ushort SubSystemMajor; + public ushort SubSystemMinor; + + public ImageDebugHeader DebugHeader; + + public Section [] Sections; + + public Section MetadataSection; + + public uint EntryPointToken; + public uint Timestamp; + public ModuleAttributes Attributes; + + public DataDirectory Win32Resources; + public DataDirectory Debug; + public DataDirectory Resources; + public DataDirectory StrongName; + + public StringHeap StringHeap; + public BlobHeap BlobHeap; + public UserStringHeap UserStringHeap; + public GuidHeap GuidHeap; + public TableHeap TableHeap; + public PdbHeap PdbHeap; + + readonly int [] coded_index_sizes = new int [14]; + + readonly Func counter; + + public Image () + { + counter = GetTableLength; + } + + public bool HasTable (Table table) + { + return GetTableLength (table) > 0; + } + + public int GetTableLength (Table table) + { + return (int)TableHeap [table].Length; + } + + public int GetTableIndexSize (Table table) + { + return GetTableLength (table) < 65536 ? 2 : 4; + } + + public int GetCodedIndexSize (CodedIndex coded_index) + { + var index = (int)coded_index; + var size = coded_index_sizes [index]; + if (size != 0) + return size; + + return coded_index_sizes [index] = coded_index.GetSize (counter); + } + + public uint ResolveVirtualAddress (RVA rva) + { + var section = GetSectionAtVirtualAddress (rva); + if (section == null) + throw new ArgumentOutOfRangeException (); + + return ResolveVirtualAddressInSection (rva, section); + } + + public uint ResolveVirtualAddressInSection (RVA rva, Section section) + { + return rva + section.PointerToRawData - section.VirtualAddress; + } + + public Section GetSection (string name) + { + var sections = this.Sections; + for (int i = 0; i < sections.Length; i++) { + var section = sections [i]; + if (section.Name == name) + return section; + } + + return null; + } + + public Section GetSectionAtVirtualAddress (RVA rva) + { + var sections = this.Sections; + for (int i = 0; i < sections.Length; i++) { + var section = sections [i]; + if (rva >= section.VirtualAddress && rva < section.VirtualAddress + section.SizeOfRawData) + return section; + } + + return null; + } + + BinaryStreamReader GetReaderAt (RVA rva) + { + var section = GetSectionAtVirtualAddress (rva); + if (section == null) + return null; + + var reader = new BinaryStreamReader (Stream.value); + reader.MoveTo (ResolveVirtualAddressInSection (rva, section)); + return reader; + } + + public TRet GetReaderAt (RVA rva, TItem item, Func read) where TRet : class + { + var position = Stream.value.Position; + try { + var reader = GetReaderAt (rva); + if (reader == null) + return null; + + return read (item, reader); + } + finally { + Stream.value.Position = position; + } + } + + public bool HasDebugTables () + { + return HasTable (Table.Document) + || HasTable (Table.MethodDebugInformation) + || HasTable (Table.LocalScope) + || HasTable (Table.LocalVariable) + || HasTable (Table.LocalConstant) + || HasTable (Table.StateMachineMethod) + || HasTable (Table.CustomDebugInformation); + } + + public void Dispose () + { + Stream.Dispose (); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/Image.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/Image.cs.meta new file mode 100644 index 0000000..2978245 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/Image.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: be7f3ca6a9f5ad34db68702bd99778fd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ImageReader.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ImageReader.cs new file mode 100644 index 0000000..8837e6b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ImageReader.cs @@ -0,0 +1,793 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.Cil; +using MonoFN.Cecil.Metadata; +using System; +using System.IO; + +namespace MonoFN.Cecil.PE { + + sealed class ImageReader : BinaryStreamReader { + + readonly Image image; + + DataDirectory cli; + DataDirectory metadata; + + uint table_heap_offset; + + public ImageReader (Disposable stream, string file_name) + : base (stream.value) + { + image = new Image (); + image.Stream = stream; + image.FileName = file_name; + } + + void MoveTo (DataDirectory directory) + { + BaseStream.Position = image.ResolveVirtualAddress (directory.VirtualAddress); + } + + void ReadImage () + { + if (BaseStream.Length < 128) + throw new BadImageFormatException (); + + // - DOSHeader + + // PE 2 + // Start 58 + // Lfanew 4 + // End 64 + + if (ReadUInt16 () != 0x5a4d) + throw new BadImageFormatException (); + + Advance (58); + + MoveTo (ReadUInt32 ()); + + if (ReadUInt32 () != 0x00004550) + throw new BadImageFormatException (); + + // - PEFileHeader + + // Machine 2 + image.Architecture = ReadArchitecture (); + + // NumberOfSections 2 + ushort sections = ReadUInt16 (); + + // TimeDateStamp 4 + image.Timestamp = ReadUInt32 (); + // PointerToSymbolTable 4 + // NumberOfSymbols 4 + // OptionalHeaderSize 2 + Advance (10); + + // Characteristics 2 + ushort characteristics = ReadUInt16 (); + + ushort subsystem, dll_characteristics; + ReadOptionalHeaders (out subsystem, out dll_characteristics); + ReadSections (sections); + ReadCLIHeader (); + ReadMetadata (); + ReadDebugHeader (); + + image.Characteristics = characteristics; + image.Kind = GetModuleKind (characteristics, subsystem); + image.DllCharacteristics = (ModuleCharacteristics)dll_characteristics; + } + + TargetArchitecture ReadArchitecture () + { + return (TargetArchitecture)ReadUInt16 (); + } + + static ModuleKind GetModuleKind (ushort characteristics, ushort subsystem) + { + if ((characteristics & 0x2000) != 0) // ImageCharacteristics.Dll + return ModuleKind.Dll; + + if (subsystem == 0x2 || subsystem == 0x9) // SubSystem.WindowsGui || SubSystem.WindowsCeGui + return ModuleKind.Windows; + + return ModuleKind.Console; + } + + void ReadOptionalHeaders (out ushort subsystem, out ushort dll_characteristics) + { + // - PEOptionalHeader + // - StandardFieldsHeader + + // Magic 2 + bool pe64 = ReadUInt16 () == 0x20b; + + // pe32 || pe64 + + image.LinkerVersion = ReadUInt16 (); + // CodeSize 4 + // InitializedDataSize 4 + // UninitializedDataSize4 + // EntryPointRVA 4 + // BaseOfCode 4 + // BaseOfData 4 || 0 + + // - NTSpecificFieldsHeader + + // ImageBase 4 || 8 + // SectionAlignment 4 + // FileAlignement 4 + // OSMajor 2 + // OSMinor 2 + // UserMajor 2 + // UserMinor 2 + // SubSysMajor 2 + // SubSysMinor 2 + Advance (44); + + image.SubSystemMajor = ReadUInt16 (); + image.SubSystemMinor = ReadUInt16 (); + + // Reserved 4 + // ImageSize 4 + // HeaderSize 4 + // FileChecksum 4 + Advance (16); + + // SubSystem 2 + subsystem = ReadUInt16 (); + + // DLLFlags 2 + dll_characteristics = ReadUInt16 (); + // StackReserveSize 4 || 8 + // StackCommitSize 4 || 8 + // HeapReserveSize 4 || 8 + // HeapCommitSize 4 || 8 + // LoaderFlags 4 + // NumberOfDataDir 4 + + // - DataDirectoriesHeader + + // ExportTable 8 + // ImportTable 8 + + Advance (pe64 ? 56 : 40); + + // ResourceTable 8 + + image.Win32Resources = ReadDataDirectory (); + + // ExceptionTable 8 + // CertificateTable 8 + // BaseRelocationTable 8 + + Advance (24); + + // Debug 8 + image.Debug = ReadDataDirectory (); + + // Copyright 8 + // GlobalPtr 8 + // TLSTable 8 + // LoadConfigTable 8 + // BoundImport 8 + // IAT 8 + // DelayImportDescriptor8 + Advance (56); + + // CLIHeader 8 + cli = ReadDataDirectory (); + + if (cli.IsZero) + throw new BadImageFormatException (); + + // Reserved 8 + Advance (8); + } + + string ReadAlignedString (int length) + { + int read = 0; + var buffer = new char [length]; + while (read < length) { + var current = ReadByte (); + if (current == 0) + break; + + buffer [read++] = (char)current; + } + + Advance (-1 + ((read + 4) & ~3) - read); + + return new string (buffer, 0, read); + } + + string ReadZeroTerminatedString (int length) + { + int read = 0; + var buffer = new char [length]; + var bytes = ReadBytes (length); + while (read < length) { + var current = bytes [read]; + if (current == 0) + break; + + buffer [read++] = (char)current; + } + + return new string (buffer, 0, read); + } + + void ReadSections (ushort count) + { + var sections = new Section [count]; + + for (int i = 0; i < count; i++) { + var section = new Section (); + + // Name + section.Name = ReadZeroTerminatedString (8); + + // VirtualSize 4 + Advance (4); + + // VirtualAddress 4 + section.VirtualAddress = ReadUInt32 (); + // SizeOfRawData 4 + section.SizeOfRawData = ReadUInt32 (); + // PointerToRawData 4 + section.PointerToRawData = ReadUInt32 (); + + // PointerToRelocations 4 + // PointerToLineNumbers 4 + // NumberOfRelocations 2 + // NumberOfLineNumbers 2 + // Characteristics 4 + Advance (16); + + sections [i] = section; + } + + image.Sections = sections; + } + + void ReadCLIHeader () + { + MoveTo (cli); + + // - CLIHeader + + // Cb 4 + // MajorRuntimeVersion 2 + // MinorRuntimeVersion 2 + Advance (8); + + // Metadata 8 + metadata = ReadDataDirectory (); + // Flags 4 + image.Attributes = (ModuleAttributes)ReadUInt32 (); + // EntryPointToken 4 + image.EntryPointToken = ReadUInt32 (); + // Resources 8 + image.Resources = ReadDataDirectory (); + // StrongNameSignature 8 + image.StrongName = ReadDataDirectory (); + // CodeManagerTable 8 + // VTableFixups 8 + // ExportAddressTableJumps 8 + // ManagedNativeHeader 8 + } + + void ReadMetadata () + { + MoveTo (metadata); + + if (ReadUInt32 () != 0x424a5342) + throw new BadImageFormatException (); + + // MajorVersion 2 + // MinorVersion 2 + // Reserved 4 + Advance (8); + + image.RuntimeVersion = ReadZeroTerminatedString (ReadInt32 ()); + + // Flags 2 + Advance (2); + + var streams = ReadUInt16 (); + + var section = image.GetSectionAtVirtualAddress (metadata.VirtualAddress); + if (section == null) + throw new BadImageFormatException (); + + image.MetadataSection = section; + + for (int i = 0; i < streams; i++) + ReadMetadataStream (section); + + if (image.PdbHeap != null) + ReadPdbHeap (); + + if (image.TableHeap != null) + ReadTableHeap (); + } + + void ReadDebugHeader () + { + if (image.Debug.IsZero) { + image.DebugHeader = new ImageDebugHeader (Empty.Array); + return; + } + + MoveTo (image.Debug); + + var entries = new ImageDebugHeaderEntry [(int)image.Debug.Size / ImageDebugDirectory.Size]; + + for (int i = 0; i < entries.Length; i++) { + var directory = new ImageDebugDirectory { + Characteristics = ReadInt32 (), + TimeDateStamp = ReadInt32 (), + MajorVersion = ReadInt16 (), + MinorVersion = ReadInt16 (), + Type = (ImageDebugType)ReadInt32 (), + SizeOfData = ReadInt32 (), + AddressOfRawData = ReadInt32 (), + PointerToRawData = ReadInt32 (), + }; + + if (directory.PointerToRawData == 0 || directory.SizeOfData < 0) { + entries [i] = new ImageDebugHeaderEntry (directory, Empty.Array); + continue; + } + + var position = Position; + try { + MoveTo ((uint)directory.PointerToRawData); + var data = ReadBytes (directory.SizeOfData); + entries [i] = new ImageDebugHeaderEntry (directory, data); + } + finally { + Position = position; + } + } + + image.DebugHeader = new ImageDebugHeader (entries); + } + + void ReadMetadataStream (Section section) + { + // Offset 4 + uint offset = metadata.VirtualAddress - section.VirtualAddress + ReadUInt32 (); // relative to the section start + + // Size 4 + uint size = ReadUInt32 (); + + var data = ReadHeapData (offset, size); + + var name = ReadAlignedString (16); + switch (name) { + case "#~": + case "#-": + image.TableHeap = new TableHeap (data); + table_heap_offset = offset; + break; + case "#Strings": + image.StringHeap = new StringHeap (data); + break; + case "#Blob": + image.BlobHeap = new BlobHeap (data); + break; + case "#GUID": + image.GuidHeap = new GuidHeap (data); + break; + case "#US": + image.UserStringHeap = new UserStringHeap (data); + break; + case "#Pdb": + image.PdbHeap = new PdbHeap (data); + break; + } + } + + byte [] ReadHeapData (uint offset, uint size) + { + var position = BaseStream.Position; + MoveTo (offset + image.MetadataSection.PointerToRawData); + var data = ReadBytes ((int)size); + BaseStream.Position = position; + + return data; + } + + void ReadTableHeap () + { + var heap = image.TableHeap; + + MoveTo (table_heap_offset + image.MetadataSection.PointerToRawData); + + // Reserved 4 + // MajorVersion 1 + // MinorVersion 1 + Advance (6); + + // HeapSizes 1 + var sizes = ReadByte (); + + // Reserved2 1 + Advance (1); + + // Valid 8 + heap.Valid = ReadInt64 (); + + // Sorted 8 + heap.Sorted = ReadInt64 (); + + if (image.PdbHeap != null) { + for (int i = 0; i < Mixin.TableCount; i++) { + if (!image.PdbHeap.HasTable ((Table)i)) + continue; + + heap.Tables [i].Length = image.PdbHeap.TypeSystemTableRows [i]; + } + } + + for (int i = 0; i < Mixin.TableCount; i++) { + if (!heap.HasTable ((Table)i)) + continue; + + heap.Tables [i].Length = ReadUInt32 (); + } + + SetIndexSize (image.StringHeap, sizes, 0x1); + SetIndexSize (image.GuidHeap, sizes, 0x2); + SetIndexSize (image.BlobHeap, sizes, 0x4); + + ComputeTableInformations (); + } + + static void SetIndexSize (Heap heap, uint sizes, byte flag) + { + if (heap == null) + return; + + heap.IndexSize = (sizes & flag) > 0 ? 4 : 2; + } + + int GetTableIndexSize (Table table) + { + return image.GetTableIndexSize (table); + } + + int GetCodedIndexSize (CodedIndex index) + { + return image.GetCodedIndexSize (index); + } + + void ComputeTableInformations () + { + uint offset = (uint)BaseStream.Position - table_heap_offset - image.MetadataSection.PointerToRawData; // header + + int stridx_size = image.StringHeap != null ? image.StringHeap.IndexSize : 2; + int guididx_size = image.GuidHeap != null ? image.GuidHeap.IndexSize : 2; + int blobidx_size = image.BlobHeap != null ? image.BlobHeap.IndexSize : 2; + + var heap = image.TableHeap; + var tables = heap.Tables; + + for (int i = 0; i < Mixin.TableCount; i++) { + var table = (Table)i; + if (!heap.HasTable (table)) + continue; + + int size; + switch (table) { + case Table.Module: + size = 2 // Generation + + stridx_size // Name + + (guididx_size * 3); // Mvid, EncId, EncBaseId + break; + case Table.TypeRef: + size = GetCodedIndexSize (CodedIndex.ResolutionScope) // ResolutionScope + + (stridx_size * 2); // Name, Namespace + break; + case Table.TypeDef: + size = 4 // Flags + + (stridx_size * 2) // Name, Namespace + + GetCodedIndexSize (CodedIndex.TypeDefOrRef) // BaseType + + GetTableIndexSize (Table.Field) // FieldList + + GetTableIndexSize (Table.Method); // MethodList + break; + case Table.FieldPtr: + size = GetTableIndexSize (Table.Field); // Field + break; + case Table.Field: + size = 2 // Flags + + stridx_size // Name + + blobidx_size; // Signature + break; + case Table.MethodPtr: + size = GetTableIndexSize (Table.Method); // Method + break; + case Table.Method: + size = 8 // Rva 4, ImplFlags 2, Flags 2 + + stridx_size // Name + + blobidx_size // Signature + + GetTableIndexSize (Table.Param); // ParamList + break; + case Table.ParamPtr: + size = GetTableIndexSize (Table.Param); // Param + break; + case Table.Param: + size = 4 // Flags 2, Sequence 2 + + stridx_size; // Name + break; + case Table.InterfaceImpl: + size = GetTableIndexSize (Table.TypeDef) // Class + + GetCodedIndexSize (CodedIndex.TypeDefOrRef); // Interface + break; + case Table.MemberRef: + size = GetCodedIndexSize (CodedIndex.MemberRefParent) // Class + + stridx_size // Name + + blobidx_size; // Signature + break; + case Table.Constant: + size = 2 // Type + + GetCodedIndexSize (CodedIndex.HasConstant) // Parent + + blobidx_size; // Value + break; + case Table.CustomAttribute: + size = GetCodedIndexSize (CodedIndex.HasCustomAttribute) // Parent + + GetCodedIndexSize (CodedIndex.CustomAttributeType) // Type + + blobidx_size; // Value + break; + case Table.FieldMarshal: + size = GetCodedIndexSize (CodedIndex.HasFieldMarshal) // Parent + + blobidx_size; // NativeType + break; + case Table.DeclSecurity: + size = 2 // Action + + GetCodedIndexSize (CodedIndex.HasDeclSecurity) // Parent + + blobidx_size; // PermissionSet + break; + case Table.ClassLayout: + size = 6 // PackingSize 2, ClassSize 4 + + GetTableIndexSize (Table.TypeDef); // Parent + break; + case Table.FieldLayout: + size = 4 // Offset + + GetTableIndexSize (Table.Field); // Field + break; + case Table.StandAloneSig: + size = blobidx_size; // Signature + break; + case Table.EventMap: + size = GetTableIndexSize (Table.TypeDef) // Parent + + GetTableIndexSize (Table.Event); // EventList + break; + case Table.EventPtr: + size = GetTableIndexSize (Table.Event); // Event + break; + case Table.Event: + size = 2 // Flags + + stridx_size // Name + + GetCodedIndexSize (CodedIndex.TypeDefOrRef); // EventType + break; + case Table.PropertyMap: + size = GetTableIndexSize (Table.TypeDef) // Parent + + GetTableIndexSize (Table.Property); // PropertyList + break; + case Table.PropertyPtr: + size = GetTableIndexSize (Table.Property); // Property + break; + case Table.Property: + size = 2 // Flags + + stridx_size // Name + + blobidx_size; // Type + break; + case Table.MethodSemantics: + size = 2 // Semantics + + GetTableIndexSize (Table.Method) // Method + + GetCodedIndexSize (CodedIndex.HasSemantics); // Association + break; + case Table.MethodImpl: + size = GetTableIndexSize (Table.TypeDef) // Class + + GetCodedIndexSize (CodedIndex.MethodDefOrRef) // MethodBody + + GetCodedIndexSize (CodedIndex.MethodDefOrRef); // MethodDeclaration + break; + case Table.ModuleRef: + size = stridx_size; // Name + break; + case Table.TypeSpec: + size = blobidx_size; // Signature + break; + case Table.ImplMap: + size = 2 // MappingFlags + + GetCodedIndexSize (CodedIndex.MemberForwarded) // MemberForwarded + + stridx_size // ImportName + + GetTableIndexSize (Table.ModuleRef); // ImportScope + break; + case Table.FieldRVA: + size = 4 // RVA + + GetTableIndexSize (Table.Field); // Field + break; + case Table.EncLog: + size = 8; + break; + case Table.EncMap: + size = 4; + break; + case Table.Assembly: + size = 16 // HashAlgId 4, Version 4 * 2, Flags 4 + + blobidx_size // PublicKey + + (stridx_size * 2); // Name, Culture + break; + case Table.AssemblyProcessor: + size = 4; // Processor + break; + case Table.AssemblyOS: + size = 12; // Platform 4, Version 2 * 4 + break; + case Table.AssemblyRef: + size = 12 // Version 2 * 4 + Flags 4 + + (blobidx_size * 2) // PublicKeyOrToken, HashValue + + (stridx_size * 2); // Name, Culture + break; + case Table.AssemblyRefProcessor: + size = 4 // Processor + + GetTableIndexSize (Table.AssemblyRef); // AssemblyRef + break; + case Table.AssemblyRefOS: + size = 12 // Platform 4, Version 2 * 4 + + GetTableIndexSize (Table.AssemblyRef); // AssemblyRef + break; + case Table.File: + size = 4 // Flags + + stridx_size // Name + + blobidx_size; // HashValue + break; + case Table.ExportedType: + size = 8 // Flags 4, TypeDefId 4 + + (stridx_size * 2) // Name, Namespace + + GetCodedIndexSize (CodedIndex.Implementation); // Implementation + break; + case Table.ManifestResource: + size = 8 // Offset, Flags + + stridx_size // Name + + GetCodedIndexSize (CodedIndex.Implementation); // Implementation + break; + case Table.NestedClass: + size = GetTableIndexSize (Table.TypeDef) // NestedClass + + GetTableIndexSize (Table.TypeDef); // EnclosingClass + break; + case Table.GenericParam: + size = 4 // Number, Flags + + GetCodedIndexSize (CodedIndex.TypeOrMethodDef) // Owner + + stridx_size; // Name + break; + case Table.MethodSpec: + size = GetCodedIndexSize (CodedIndex.MethodDefOrRef) // Method + + blobidx_size; // Instantiation + break; + case Table.GenericParamConstraint: + size = GetTableIndexSize (Table.GenericParam) // Owner + + GetCodedIndexSize (CodedIndex.TypeDefOrRef); // Constraint + break; + case Table.Document: + size = blobidx_size // Name + + guididx_size // HashAlgorithm + + blobidx_size // Hash + + guididx_size; // Language + break; + case Table.MethodDebugInformation: + size = GetTableIndexSize (Table.Document) // Document + + blobidx_size; // SequencePoints + break; + case Table.LocalScope: + size = GetTableIndexSize (Table.Method) // Method + + GetTableIndexSize (Table.ImportScope) // ImportScope + + GetTableIndexSize (Table.LocalVariable) // VariableList + + GetTableIndexSize (Table.LocalConstant) // ConstantList + + 4 * 2; // StartOffset, Length + break; + case Table.LocalVariable: + size = 2 // Attributes + + 2 // Index + + stridx_size; // Name + break; + case Table.LocalConstant: + size = stridx_size // Name + + blobidx_size; // Signature + break; + case Table.ImportScope: + size = GetTableIndexSize (Table.ImportScope) // Parent + + blobidx_size; + break; + case Table.StateMachineMethod: + size = GetTableIndexSize (Table.Method) // MoveNextMethod + + GetTableIndexSize (Table.Method); // KickOffMethod + break; + case Table.CustomDebugInformation: + size = GetCodedIndexSize (CodedIndex.HasCustomDebugInformation) // Parent + + guididx_size // Kind + + blobidx_size; // Value + break; + default: + throw new NotSupportedException (); + } + + tables [i].RowSize = (uint)size; + tables [i].Offset = offset; + + offset += (uint)size * tables [i].Length; + } + } + + void ReadPdbHeap () + { + var heap = image.PdbHeap; + + var buffer = new ByteBuffer (heap.data); + + heap.Id = buffer.ReadBytes (20); + heap.EntryPoint = buffer.ReadUInt32 (); + heap.TypeSystemTables = buffer.ReadInt64 (); + heap.TypeSystemTableRows = new uint [Mixin.TableCount]; + + for (int i = 0; i < Mixin.TableCount; i++) { + var table = (Table)i; + if (!heap.HasTable (table)) + continue; + + heap.TypeSystemTableRows [i] = buffer.ReadUInt32 (); + } + } + + public static Image ReadImage (Disposable stream, string file_name) + { + try { + var reader = new ImageReader (stream, file_name); + reader.ReadImage (); + return reader.image; + } + catch (EndOfStreamException e) { + throw new BadImageFormatException (stream.value.GetFileName (), e); + } + } + + public static Image ReadPortablePdb (Disposable stream, string file_name) + { + try { + var reader = new ImageReader (stream, file_name); + var length = (uint)stream.value.Length; + + reader.image.Sections = new [] { + new Section { + PointerToRawData = 0, + SizeOfRawData = length, + VirtualAddress = 0, + VirtualSize = length, + } + }; + + reader.metadata = new DataDirectory (0, length); + reader.ReadMetadata (); + return reader.image; + } + catch (EndOfStreamException e) { + throw new BadImageFormatException (stream.value.GetFileName (), e); + } + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ImageReader.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ImageReader.cs.meta new file mode 100644 index 0000000..a001d10 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ImageReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ba04d4e3389423e47b35aaccee205576 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ImageWriter.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ImageWriter.cs new file mode 100644 index 0000000..061a95f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ImageWriter.cs @@ -0,0 +1,860 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.Cil; +using MonoFN.Cecil.Metadata; +using System; +using System.IO; +using RVA = System.UInt32; + +namespace MonoFN.Cecil.PE { + + sealed class ImageWriter : BinaryStreamWriter { + + readonly ModuleDefinition module; + readonly MetadataBuilder metadata; + readonly TextMap text_map; + readonly internal Disposable stream; + + readonly string runtime_version; + + ImageDebugHeader debug_header; + + ByteBuffer win32_resources; + + const uint pe_header_size = 0x98u; + const uint section_header_size = 0x28u; + const uint file_alignment = 0x200; + const uint section_alignment = 0x2000; + const ulong image_base = 0x00400000; + + internal const RVA text_rva = 0x2000; + + readonly bool pe64; + readonly bool has_reloc; + + internal Section text; + internal Section rsrc; + internal Section reloc; + + ushort sections; + + ImageWriter (ModuleDefinition module, string runtime_version, MetadataBuilder metadata, Disposable stream, bool metadataOnly = false) + : base (stream.value) + { + this.module = module; + this.runtime_version = runtime_version; + this.text_map = metadata.text_map; + this.stream = stream; + this.metadata = metadata; + if (metadataOnly) + return; + + this.pe64 = module.Architecture == TargetArchitecture.AMD64 || module.Architecture == TargetArchitecture.IA64 || module.Architecture == TargetArchitecture.ARM64; + this.has_reloc = module.Architecture == TargetArchitecture.I386; + this.GetDebugHeader (); + this.GetWin32Resources (); + this.BuildTextMap (); + this.sections = (ushort)(has_reloc ? 2 : 1); // text + reloc? + } + + void GetDebugHeader () + { + var symbol_writer = metadata.symbol_writer; + if (symbol_writer != null) + debug_header = symbol_writer.GetDebugHeader (); + + if (module.HasDebugHeader) { + var header = module.GetDebugHeader (); + var deterministic = header.GetDeterministicEntry (); + if (deterministic == null) + return; + + debug_header = debug_header.AddDeterministicEntry (); + } + } + + void GetWin32Resources () + { + if (!module.HasImage) + return; + + DataDirectory win32_resources_directory = module.Image.Win32Resources; + var size = win32_resources_directory.Size; + + if (size > 0) { + win32_resources = module.Image.GetReaderAt (win32_resources_directory.VirtualAddress, size, (s, reader) => new ByteBuffer (reader.ReadBytes ((int)s))); + } + } + + public static ImageWriter CreateWriter (ModuleDefinition module, MetadataBuilder metadata, Disposable stream) + { + var writer = new ImageWriter (module, module.runtime_version, metadata, stream); + writer.BuildSections (); + return writer; + } + + public static ImageWriter CreateDebugWriter (ModuleDefinition module, MetadataBuilder metadata, Disposable stream) + { + var writer = new ImageWriter (module, "PDB v1.0", metadata, stream, metadataOnly: true); + var length = metadata.text_map.GetLength (); + writer.text = new Section { SizeOfRawData = length, VirtualSize = length }; + return writer; + } + + void BuildSections () + { + var has_win32_resources = win32_resources != null; + if (has_win32_resources) + sections++; + + text = CreateSection (".text", text_map.GetLength (), null); + var previous = text; + + if (has_win32_resources) { + rsrc = CreateSection (".rsrc", (uint)win32_resources.length, previous); + + PatchWin32Resources (win32_resources); + previous = rsrc; + } + + if (has_reloc) + reloc = CreateSection (".reloc", 12u, previous); + } + + Section CreateSection (string name, uint size, Section previous) + { + return new Section { + Name = name, + VirtualAddress = previous != null + ? previous.VirtualAddress + Align (previous.VirtualSize, section_alignment) + : text_rva, + VirtualSize = size, + PointerToRawData = previous != null + ? previous.PointerToRawData + previous.SizeOfRawData + : Align (GetHeaderSize (), file_alignment), + SizeOfRawData = Align (size, file_alignment) + }; + } + + static uint Align (uint value, uint align) + { + align--; + return (value + align) & ~align; + } + + void WriteDOSHeader () + { + Write (new byte [] { + // dos header start + 0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + // lfanew + 0x80, 0x00, 0x00, 0x00, + // dos header end + 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, + 0xcd, 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, + 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, + 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x20, 0x63, + 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x62, + 0x65, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, + 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, 0x6d, + 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 + }); + } + + ushort SizeOfOptionalHeader () + { + return (ushort)(!pe64 ? 0xe0 : 0xf0); + } + + void WritePEFileHeader () + { + WriteUInt32 (0x00004550); // Magic + WriteUInt16 ((ushort)module.Architecture); // Machine + WriteUInt16 (sections); // NumberOfSections + WriteUInt32 (metadata.timestamp); + WriteUInt32 (0); // PointerToSymbolTable + WriteUInt32 (0); // NumberOfSymbols + WriteUInt16 (SizeOfOptionalHeader ()); // SizeOfOptionalHeader + + const ushort LargeAddressAware = 0x0020; + + // ExecutableImage | (!pe64 ? 32BitsMachine : LargeAddressAware) + var characteristics = (ushort)(0x0002 | (!pe64 ? 0x0100 : LargeAddressAware)); + if (module.Kind == ModuleKind.Dll || module.Kind == ModuleKind.NetModule) + characteristics |= 0x2000; + + if (module.Image != null && (module.Image.Characteristics & LargeAddressAware) != 0) + characteristics |= LargeAddressAware; + + WriteUInt16 (characteristics); // Characteristics + } + + Section LastSection () + { + if (reloc != null) + return reloc; + + if (rsrc != null) + return rsrc; + + return text; + } + + void WriteOptionalHeaders () + { + WriteUInt16 ((ushort)(!pe64 ? 0x10b : 0x20b)); // Magic + WriteUInt16 (module.linker_version); + WriteUInt32 (text.SizeOfRawData); // CodeSize + WriteUInt32 ((reloc != null ? reloc.SizeOfRawData : 0) + + (rsrc != null ? rsrc.SizeOfRawData : 0)); // InitializedDataSize + WriteUInt32 (0); // UninitializedDataSize + + var startub_stub = text_map.GetRange (TextSegment.StartupStub); + WriteUInt32 (startub_stub.Length > 0 ? startub_stub.Start : 0); // EntryPointRVA + WriteUInt32 (text_rva); // BaseOfCode + + if (!pe64) { + WriteUInt32 (0); // BaseOfData + WriteUInt32 ((uint)image_base); // ImageBase + } else { + WriteUInt64 (image_base); // ImageBase + } + + WriteUInt32 (section_alignment); // SectionAlignment + WriteUInt32 (file_alignment); // FileAlignment + + WriteUInt16 (4); // OSMajor + WriteUInt16 (0); // OSMinor + WriteUInt16 (0); // UserMajor + WriteUInt16 (0); // UserMinor + WriteUInt16 (module.subsystem_major); // SubSysMajor + WriteUInt16 (module.subsystem_minor); // SubSysMinor + WriteUInt32 (0); // Reserved + + var last_section = LastSection (); + WriteUInt32 (last_section.VirtualAddress + Align (last_section.VirtualSize, section_alignment)); // ImageSize + WriteUInt32 (text.PointerToRawData); // HeaderSize + + WriteUInt32 (0); // Checksum + WriteUInt16 (GetSubSystem ()); // SubSystem + WriteUInt16 ((ushort)module.Characteristics); // DLLFlags + + if (!pe64) { + const uint stack_reserve = 0x100000; + const uint stack_commit = 0x1000; + const uint heap_reserve = 0x100000; + const uint heap_commit = 0x1000; + + WriteUInt32 (stack_reserve); + WriteUInt32 (stack_commit); + WriteUInt32 (heap_reserve); + WriteUInt32 (heap_commit); + } else { + const ulong stack_reserve = 0x400000; + const ulong stack_commit = 0x4000; + const ulong heap_reserve = 0x100000; + const ulong heap_commit = 0x2000; + + WriteUInt64 (stack_reserve); + WriteUInt64 (stack_commit); + WriteUInt64 (heap_reserve); + WriteUInt64 (heap_commit); + } + + WriteUInt32 (0); // LoaderFlags + WriteUInt32 (16); // NumberOfDataDir + + WriteZeroDataDirectory (); // ExportTable + WriteDataDirectory (text_map.GetDataDirectory (TextSegment.ImportDirectory)); // ImportTable + if (rsrc != null) { // ResourceTable + WriteUInt32 (rsrc.VirtualAddress); + WriteUInt32 (rsrc.VirtualSize); + } else + WriteZeroDataDirectory (); + + WriteZeroDataDirectory (); // ExceptionTable + WriteZeroDataDirectory (); // CertificateTable + WriteUInt32 (reloc != null ? reloc.VirtualAddress : 0); // BaseRelocationTable + WriteUInt32 (reloc != null ? reloc.VirtualSize : 0); + + if (text_map.GetLength (TextSegment.DebugDirectory) > 0) { + WriteUInt32 (text_map.GetRVA (TextSegment.DebugDirectory)); + WriteUInt32 ((uint)(debug_header.Entries.Length * ImageDebugDirectory.Size)); + } else + WriteZeroDataDirectory (); + + WriteZeroDataDirectory (); // Copyright + WriteZeroDataDirectory (); // GlobalPtr + WriteZeroDataDirectory (); // TLSTable + WriteZeroDataDirectory (); // LoadConfigTable + WriteZeroDataDirectory (); // BoundImport + WriteDataDirectory (text_map.GetDataDirectory (TextSegment.ImportAddressTable)); // IAT + WriteZeroDataDirectory (); // DelayImportDesc + WriteDataDirectory (text_map.GetDataDirectory (TextSegment.CLIHeader)); // CLIHeader + WriteZeroDataDirectory (); // Reserved + } + + void WriteZeroDataDirectory () + { + WriteUInt32 (0); + WriteUInt32 (0); + } + + ushort GetSubSystem () + { + switch (module.Kind) { + case ModuleKind.Console: + case ModuleKind.Dll: + case ModuleKind.NetModule: + return 0x3; + case ModuleKind.Windows: + return 0x2; + default: + throw new ArgumentOutOfRangeException (); + } + } + + void WriteSectionHeaders () + { + WriteSection (text, 0x60000020); + + if (rsrc != null) + WriteSection (rsrc, 0x40000040); + + if (reloc != null) + WriteSection (reloc, 0x42000040); + } + + void WriteSection (Section section, uint characteristics) + { + var name = new byte [8]; + var sect_name = section.Name; + for (int i = 0; i < sect_name.Length; i++) + name [i] = (byte)sect_name [i]; + + WriteBytes (name); + WriteUInt32 (section.VirtualSize); + WriteUInt32 (section.VirtualAddress); + WriteUInt32 (section.SizeOfRawData); + WriteUInt32 (section.PointerToRawData); + WriteUInt32 (0); // PointerToRelocations + WriteUInt32 (0); // PointerToLineNumbers + WriteUInt16 (0); // NumberOfRelocations + WriteUInt16 (0); // NumberOfLineNumbers + WriteUInt32 (characteristics); + } + + uint GetRVAFileOffset (Section section, RVA rva) + { + return section.PointerToRawData + rva - section.VirtualAddress; + } + + void MoveTo (uint pointer) + { + BaseStream.Seek (pointer, SeekOrigin.Begin); + } + + void MoveToRVA (Section section, RVA rva) + { + BaseStream.Seek (GetRVAFileOffset (section, rva), SeekOrigin.Begin); + } + + void MoveToRVA (TextSegment segment) + { + MoveToRVA (text, text_map.GetRVA (segment)); + } + + void WriteRVA (RVA rva) + { + if (!pe64) + WriteUInt32 (rva); + else + WriteUInt64 (rva); + } + + void PrepareSection (Section section) + { + MoveTo (section.PointerToRawData); + + const int buffer_size = 4096; + + if (section.SizeOfRawData <= buffer_size) { + Write (new byte [section.SizeOfRawData]); + MoveTo (section.PointerToRawData); + return; + } + + var written = 0; + var buffer = new byte [buffer_size]; + while (written != section.SizeOfRawData) { + var write_size = System.Math.Min ((int)section.SizeOfRawData - written, buffer_size); + Write (buffer, 0, write_size); + written += write_size; + } + + MoveTo (section.PointerToRawData); + } + + void WriteText () + { + PrepareSection (text); + + // ImportAddressTable + + if (has_reloc) { + WriteRVA (text_map.GetRVA (TextSegment.ImportHintNameTable)); + WriteRVA (0); + } + + // CLIHeader + + WriteUInt32 (0x48); + WriteUInt16 (2); + WriteUInt16 ((ushort)((module.Runtime <= TargetRuntime.Net_1_1) ? 0 : 5)); + + WriteUInt32 (text_map.GetRVA (TextSegment.MetadataHeader)); + WriteUInt32 (GetMetadataLength ()); + WriteUInt32 ((uint)module.Attributes); + WriteUInt32 (metadata.entry_point.ToUInt32 ()); + WriteDataDirectory (text_map.GetDataDirectory (TextSegment.Resources)); + WriteDataDirectory (text_map.GetDataDirectory (TextSegment.StrongNameSignature)); + WriteZeroDataDirectory (); // CodeManagerTable + WriteZeroDataDirectory (); // VTableFixups + WriteZeroDataDirectory (); // ExportAddressTableJumps + WriteZeroDataDirectory (); // ManagedNativeHeader + + // Code + + MoveToRVA (TextSegment.Code); + WriteBuffer (metadata.code); + + // Resources + + MoveToRVA (TextSegment.Resources); + WriteBuffer (metadata.resources); + + // Data + + if (metadata.data.length > 0) { + MoveToRVA (TextSegment.Data); + WriteBuffer (metadata.data); + } + + // StrongNameSignature + // stays blank + + // MetadataHeader + + MoveToRVA (TextSegment.MetadataHeader); + WriteMetadataHeader (); + + WriteMetadata (); + + // DebugDirectory + if (text_map.GetLength (TextSegment.DebugDirectory) > 0) { + MoveToRVA (TextSegment.DebugDirectory); + WriteDebugDirectory (); + } + + if (!has_reloc) + return; + + // ImportDirectory + MoveToRVA (TextSegment.ImportDirectory); + WriteImportDirectory (); + + // StartupStub + MoveToRVA (TextSegment.StartupStub); + WriteStartupStub (); + } + + uint GetMetadataLength () + { + return text_map.GetRVA (TextSegment.DebugDirectory) - text_map.GetRVA (TextSegment.MetadataHeader); + } + + public void WriteMetadataHeader () + { + WriteUInt32 (0x424a5342); // Signature + WriteUInt16 (1); // MajorVersion + WriteUInt16 (1); // MinorVersion + WriteUInt32 (0); // Reserved + + var version = GetZeroTerminatedString (runtime_version); + WriteUInt32 ((uint)version.Length); + WriteBytes (version); + WriteUInt16 (0); // Flags + WriteUInt16 (GetStreamCount ()); + + uint offset = text_map.GetRVA (TextSegment.TableHeap) - text_map.GetRVA (TextSegment.MetadataHeader); + + WriteStreamHeader (ref offset, TextSegment.TableHeap, "#~"); + WriteStreamHeader (ref offset, TextSegment.StringHeap, "#Strings"); + WriteStreamHeader (ref offset, TextSegment.UserStringHeap, "#US"); + WriteStreamHeader (ref offset, TextSegment.GuidHeap, "#GUID"); + WriteStreamHeader (ref offset, TextSegment.BlobHeap, "#Blob"); + WriteStreamHeader (ref offset, TextSegment.PdbHeap, "#Pdb"); + } + + ushort GetStreamCount () + { + return (ushort)( + 1 // #~ + + 1 // #Strings + + (metadata.user_string_heap.IsEmpty ? 0 : 1) // #US + + (metadata.guid_heap.IsEmpty ? 0 : 1) // GUID + + (metadata.blob_heap.IsEmpty ? 0 : 1) + + (metadata.pdb_heap == null ? 0 : 1)); // #Blob + } + + void WriteStreamHeader (ref uint offset, TextSegment heap, string name) + { + var length = (uint)text_map.GetLength (heap); + if (length == 0) + return; + + WriteUInt32 (offset); + WriteUInt32 (length); + WriteBytes (GetZeroTerminatedString (name)); + offset += length; + } + + static int GetZeroTerminatedStringLength (string @string) + { + return (@string.Length + 1 + 3) & ~3; + } + + static byte [] GetZeroTerminatedString (string @string) + { + return GetString (@string, GetZeroTerminatedStringLength (@string)); + } + + static byte [] GetSimpleString (string @string) + { + return GetString (@string, @string.Length); + } + + static byte [] GetString (string @string, int length) + { + var bytes = new byte [length]; + for (int i = 0; i < @string.Length; i++) + bytes [i] = (byte)@string [i]; + + return bytes; + } + + public void WriteMetadata () + { + WriteHeap (TextSegment.TableHeap, metadata.table_heap); + WriteHeap (TextSegment.StringHeap, metadata.string_heap); + WriteHeap (TextSegment.UserStringHeap, metadata.user_string_heap); + WriteHeap (TextSegment.GuidHeap, metadata.guid_heap); + WriteHeap (TextSegment.BlobHeap, metadata.blob_heap); + WriteHeap (TextSegment.PdbHeap, metadata.pdb_heap); + } + + void WriteHeap (TextSegment heap, HeapBuffer buffer) + { + if (buffer == null || buffer.IsEmpty) + return; + + MoveToRVA (heap); + WriteBuffer (buffer); + } + + void WriteDebugDirectory () + { + var data_start = (int)BaseStream.Position + (debug_header.Entries.Length * ImageDebugDirectory.Size); + + for (var i = 0; i < debug_header.Entries.Length; i++) { + var entry = debug_header.Entries [i]; + var directory = entry.Directory; + WriteInt32 (directory.Characteristics); + WriteInt32 (directory.TimeDateStamp); + WriteInt16 (directory.MajorVersion); + WriteInt16 (directory.MinorVersion); + WriteInt32 ((int)directory.Type); + WriteInt32 (directory.SizeOfData); + WriteInt32 (directory.AddressOfRawData); + WriteInt32 (data_start); + + data_start += entry.Data.Length; + } + + for (var i = 0; i < debug_header.Entries.Length; i++) { + var entry = debug_header.Entries [i]; + WriteBytes (entry.Data); + } + } + + void WriteImportDirectory () + { + WriteUInt32 (text_map.GetRVA (TextSegment.ImportDirectory) + 40); // ImportLookupTable + WriteUInt32 (0); // DateTimeStamp + WriteUInt32 (0); // ForwarderChain + WriteUInt32 (text_map.GetRVA (TextSegment.ImportHintNameTable) + 14); + WriteUInt32 (text_map.GetRVA (TextSegment.ImportAddressTable)); + Advance (20); + + // ImportLookupTable + WriteUInt32 (text_map.GetRVA (TextSegment.ImportHintNameTable)); + + // ImportHintNameTable + MoveToRVA (TextSegment.ImportHintNameTable); + + WriteUInt16 (0); // Hint + WriteBytes (GetRuntimeMain ()); + WriteByte (0); + WriteBytes (GetSimpleString ("mscoree.dll")); + WriteUInt16 (0); + } + + byte [] GetRuntimeMain () + { + return module.Kind == ModuleKind.Dll || module.Kind == ModuleKind.NetModule + ? GetSimpleString ("_CorDllMain") + : GetSimpleString ("_CorExeMain"); + } + + void WriteStartupStub () + { + switch (module.Architecture) { + case TargetArchitecture.I386: + WriteUInt16 (0x25ff); + WriteUInt32 ((uint)image_base + text_map.GetRVA (TextSegment.ImportAddressTable)); + return; + default: + throw new NotSupportedException (); + } + } + + void WriteRsrc () + { + PrepareSection (rsrc); + WriteBuffer (win32_resources); + } + + void WriteReloc () + { + PrepareSection (reloc); + + var reloc_rva = text_map.GetRVA (TextSegment.StartupStub); + reloc_rva += module.Architecture == TargetArchitecture.IA64 ? 0x20u : 2; + var page_rva = reloc_rva & ~0xfffu; + + WriteUInt32 (page_rva); // PageRVA + WriteUInt32 (0x000c); // Block Size + + switch (module.Architecture) { + case TargetArchitecture.I386: + WriteUInt32 (0x3000 + reloc_rva - page_rva); + break; + default: + throw new NotSupportedException (); + } + } + + public void WriteImage () + { + WriteDOSHeader (); + WritePEFileHeader (); + WriteOptionalHeaders (); + WriteSectionHeaders (); + WriteText (); + if (rsrc != null) + WriteRsrc (); + if (reloc != null) + WriteReloc (); + Flush (); + } + + void BuildTextMap () + { + var map = text_map; + + map.AddMap (TextSegment.Code, metadata.code.length, !pe64 ? 4 : 16); + map.AddMap (TextSegment.Resources, metadata.resources.length, 8); + map.AddMap (TextSegment.Data, metadata.data.length, 4); + if (metadata.data.length > 0) + metadata.table_heap.FixupData (map.GetRVA (TextSegment.Data)); + map.AddMap (TextSegment.StrongNameSignature, GetStrongNameLength (), 4); + + BuildMetadataTextMap (); + + int debug_dir_len = 0; + if (debug_header != null && debug_header.HasEntries) { + var directories_len = debug_header.Entries.Length * ImageDebugDirectory.Size; + var data_address = (int)map.GetNextRVA (TextSegment.BlobHeap) + directories_len; + var data_len = 0; + + for (var i = 0; i < debug_header.Entries.Length; i++) { + var entry = debug_header.Entries [i]; + var directory = entry.Directory; + + directory.AddressOfRawData = entry.Data.Length == 0 ? 0 : data_address; + entry.Directory = directory; + + data_len += entry.Data.Length; + data_address += data_len; + } + + debug_dir_len = directories_len + data_len; + } + + map.AddMap (TextSegment.DebugDirectory, debug_dir_len, 4); + + if (!has_reloc) { + var start = map.GetNextRVA (TextSegment.DebugDirectory); + map.AddMap (TextSegment.ImportDirectory, new Range (start, 0)); + map.AddMap (TextSegment.ImportHintNameTable, new Range (start, 0)); + map.AddMap (TextSegment.StartupStub, new Range (start, 0)); + return; + } + + RVA import_dir_rva = map.GetNextRVA (TextSegment.DebugDirectory); + RVA import_hnt_rva = import_dir_rva + 48u; + import_hnt_rva = (import_hnt_rva + 15u) & ~15u; + uint import_dir_len = (import_hnt_rva - import_dir_rva) + 27u; + + RVA startup_stub_rva = import_dir_rva + import_dir_len; + startup_stub_rva = module.Architecture == TargetArchitecture.IA64 + ? (startup_stub_rva + 15u) & ~15u + : 2 + ((startup_stub_rva + 3u) & ~3u); + + map.AddMap (TextSegment.ImportDirectory, new Range (import_dir_rva, import_dir_len)); + map.AddMap (TextSegment.ImportHintNameTable, new Range (import_hnt_rva, 0)); + map.AddMap (TextSegment.StartupStub, new Range (startup_stub_rva, GetStartupStubLength ())); + } + + public void BuildMetadataTextMap () + { + var map = text_map; + + map.AddMap (TextSegment.MetadataHeader, GetMetadataHeaderLength (module.RuntimeVersion)); + map.AddMap (TextSegment.TableHeap, metadata.table_heap.length, 4); + map.AddMap (TextSegment.StringHeap, metadata.string_heap.length, 4); + map.AddMap (TextSegment.UserStringHeap, metadata.user_string_heap.IsEmpty ? 0 : metadata.user_string_heap.length, 4); + map.AddMap (TextSegment.GuidHeap, metadata.guid_heap.length, 4); + map.AddMap (TextSegment.BlobHeap, metadata.blob_heap.IsEmpty ? 0 : metadata.blob_heap.length, 4); + map.AddMap (TextSegment.PdbHeap, metadata.pdb_heap == null ? 0 : metadata.pdb_heap.length, 4); + } + + uint GetStartupStubLength () + { + switch (module.Architecture) { + case TargetArchitecture.I386: + return 6; + default: + throw new NotSupportedException (); + } + } + + int GetMetadataHeaderLength (string runtimeVersion) + { + return + // MetadataHeader + 20 + GetZeroTerminatedStringLength (runtimeVersion) + // #~ header + + 12 + // #Strings header + + 20 + // #US header + + (metadata.user_string_heap.IsEmpty ? 0 : 12) + // #GUID header + + 16 + // #Blob header + + (metadata.blob_heap.IsEmpty ? 0 : 16) + // + + (metadata.pdb_heap == null ? 0 : 16); + } + + int GetStrongNameLength () + { + if (module.kind == ModuleKind.NetModule || module.Assembly == null) + return 0; + + var public_key = module.Assembly.Name.PublicKey; + if (public_key.IsNullOrEmpty ()) + return 0; + + // in fx 2.0 the key may be from 384 to 16384 bits + // so we must calculate the signature size based on + // the size of the public key (minus the 32 byte header) + int size = public_key.Length; + if (size > 32) + return size - 32; + + // note: size == 16 for the ECMA "key" which is replaced + // by the runtime with a 1024 bits key (128 bytes) + + return 128; // default strongname signature size + } + + public DataDirectory GetStrongNameSignatureDirectory () + { + return text_map.GetDataDirectory (TextSegment.StrongNameSignature); + } + + public uint GetHeaderSize () + { + return pe_header_size + SizeOfOptionalHeader () + (sections * section_header_size); + } + + void PatchWin32Resources (ByteBuffer resources) + { + PatchResourceDirectoryTable (resources); + } + + void PatchResourceDirectoryTable (ByteBuffer resources) + { + resources.Advance (12); + + var entries = resources.ReadUInt16 () + resources.ReadUInt16 (); + + for (int i = 0; i < entries; i++) + PatchResourceDirectoryEntry (resources); + } + + void PatchResourceDirectoryEntry (ByteBuffer resources) + { + resources.Advance (4); + var child = resources.ReadUInt32 (); + + var position = resources.position; + resources.position = (int)child & 0x7fffffff; + + if ((child & 0x80000000) != 0) + PatchResourceDirectoryTable (resources); + else + PatchResourceDataEntry (resources); + + resources.position = position; + } + + void PatchResourceDataEntry (ByteBuffer resources) + { + var rva = resources.ReadUInt32 (); + resources.position -= 4; + + resources.WriteUInt32 (rva - module.Image.Win32Resources.VirtualAddress + rsrc.VirtualAddress); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ImageWriter.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ImageWriter.cs.meta new file mode 100644 index 0000000..c230acb --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ImageWriter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 88ebae078ed1c8346be0a945ce3b03b0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/Section.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/Section.cs new file mode 100644 index 0000000..9c52503 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/Section.cs @@ -0,0 +1,22 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using RVA = System.UInt32; + +namespace MonoFN.Cecil.PE { + + sealed class Section { + public string Name; + public RVA VirtualAddress; + public uint VirtualSize; + public uint SizeOfRawData; + public uint PointerToRawData; + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/Section.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/Section.cs.meta new file mode 100644 index 0000000..33d91fb --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/Section.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dc33915bdaaf42a428f8a4694e9611a8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/TextMap.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/TextMap.cs new file mode 100644 index 0000000..fbe06e2 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/TextMap.cs @@ -0,0 +1,106 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using RVA = System.UInt32; + +namespace MonoFN.Cecil.PE { + + enum TextSegment { + ImportAddressTable, + CLIHeader, + Code, + Resources, + Data, + StrongNameSignature, + + // Metadata + MetadataHeader, + TableHeap, + StringHeap, + UserStringHeap, + GuidHeap, + BlobHeap, + PdbHeap, + // End Metadata + + DebugDirectory, + ImportDirectory, + ImportHintNameTable, + StartupStub, + } + + sealed class TextMap { + + readonly Range [] map = new Range [17 /*Enum.GetValues (typeof (TextSegment)).Length*/]; + + public void AddMap (TextSegment segment, int length) + { + map [(int)segment] = new Range (GetStart (segment), (uint)length); + } + + public void AddMap (TextSegment segment, int length, int align) + { + align--; + + AddMap (segment, (length + align) & ~align); + } + + public void AddMap (TextSegment segment, Range range) + { + map [(int)segment] = range; + } + + public Range GetRange (TextSegment segment) + { + return map [(int)segment]; + } + + public DataDirectory GetDataDirectory (TextSegment segment) + { + var range = map [(int)segment]; + + return new DataDirectory (range.Length == 0 ? 0 : range.Start, range.Length); + } + + public RVA GetRVA (TextSegment segment) + { + return map [(int)segment].Start; + } + + public RVA GetNextRVA (TextSegment segment) + { + var i = (int)segment; + return map [i].Start + map [i].Length; + } + + public int GetLength (TextSegment segment) + { + return (int)map [(int)segment].Length; + } + + RVA GetStart (TextSegment segment) + { + var index = (int)segment; + return index == 0 ? ImageWriter.text_rva : ComputeStart (index); + } + + RVA ComputeStart (int index) + { + index--; + return map [index].Start + map [index].Length; + } + + public uint GetLength () + { + var range = map [(int)TextSegment.StartupStub]; + return range.Start - ImageWriter.text_rva + range.Length; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/TextMap.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/TextMap.cs.meta new file mode 100644 index 0000000..93ceea3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/TextMap.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1d36ca0589eb8014fa28bc58a88ae85f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Tests.props b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Tests.props new file mode 100644 index 0000000..bd6df1f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Tests.props @@ -0,0 +1,16 @@ + + + true + + + + 3.11.0 + + + 15.9.0 + + + 3.12.0 + + + diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Tests.props.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Tests.props.meta new file mode 100644 index 0000000..ae02e4c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Tests.props.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 4304d9110a6c73049a70f3d001e1ac37 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.meta new file mode 100644 index 0000000..835dc15 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9a32c41438af560498f0e8ae5548097a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.nunit b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.nunit new file mode 100644 index 0000000..2acfc12 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.nunit @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.nunit.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.nunit.meta new file mode 100644 index 0000000..0163c57 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.nunit.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 70249fc3714c2ba43bb69eeceaf02171 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.nuspec b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.nuspec new file mode 100644 index 0000000..626c5ac --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.nuspec @@ -0,0 +1,42 @@ + + + + Mono.Cecil + 0.11.4.0 + Mono.Cecil + Jb Evain + Jb Evain + MIT + false + http://github.com/jbevain/cecil/ + Cecil is a library written by Jb Evain to generate and inspect programs and libraries in the ECMA CIL format. + Cecil is a library written by Jb Evain to generate and inspect programs and libraries in the ECMA CIL format. It has full support for generics, and support some debugging symbol format. In simple English, with Cecil, you can load existing managed assemblies, browse all the contained types, modify them on the fly and save back to the disk the modified assembly. + en-US + assembly assemblies module modules il cil msil bytecode reflection injection cecil mono aop + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.nuspec.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.nuspec.meta new file mode 100644 index 0000000..033480e --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.nuspec.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9ab5a3af6caf6d14da0bad821a809a27 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.sln.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.sln.meta new file mode 100644 index 0000000..aa8a077 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.sln.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 246f31a0e00fea74a93125fec6d80da8 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ArrayType.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ArrayType.cs new file mode 100644 index 0000000..df72aab --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ArrayType.cs @@ -0,0 +1,145 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System; +using System.Text; +using System.Threading; +using MD = MonoFN.Cecil.Metadata; + +namespace MonoFN.Cecil { + + public struct ArrayDimension { + + int? lower_bound; + int? upper_bound; + + public int? LowerBound { + get { return lower_bound; } + set { lower_bound = value; } + } + + public int? UpperBound { + get { return upper_bound; } + set { upper_bound = value; } + } + + public bool IsSized { + get { return lower_bound.HasValue || upper_bound.HasValue; } + } + + public ArrayDimension (int? lowerBound, int? upperBound) + { + this.lower_bound = lowerBound; + this.upper_bound = upperBound; + } + + public override string ToString () + { + return !IsSized + ? string.Empty + : lower_bound + "..." + upper_bound; + } + } + + public sealed class ArrayType : TypeSpecification { + + Collection dimensions; + + public Collection Dimensions { + get { + if (dimensions != null) + return dimensions; + + var empty_dimensions = new Collection (); + empty_dimensions.Add (new ArrayDimension ()); + + Interlocked.CompareExchange (ref dimensions, empty_dimensions, null); + + return dimensions; + } + } + + public int Rank { + get { return dimensions == null ? 1 : dimensions.Count; } + } + + public bool IsVector { + get { + if (dimensions == null) + return true; + + if (dimensions.Count > 1) + return false; + + var dimension = dimensions [0]; + + return !dimension.IsSized; + } + } + + public override bool IsValueType { + get { return false; } + set { throw new InvalidOperationException (); } + } + + public override string Name { + get { return base.Name + Suffix; } + } + + public override string FullName { + get { return base.FullName + Suffix; } + } + + string Suffix { + get { + if (IsVector) + return "[]"; + + var suffix = new StringBuilder (); + suffix.Append ("["); + for (int i = 0; i < dimensions.Count; i++) { + if (i > 0) + suffix.Append (","); + + suffix.Append (dimensions [i].ToString ()); + } + suffix.Append ("]"); + + return suffix.ToString (); + } + } + + public override bool IsArray { + get { return true; } + } + + public ArrayType (TypeReference type) + : base (type) + { + Mixin.CheckType (type); + this.etype = MD.ElementType.Array; + } + + public ArrayType (TypeReference type, int rank) + : this (type) + { + Mixin.CheckType (type); + + if (rank == 1) + return; + + dimensions = new Collection (rank); + for (int i = 0; i < rank; i++) + dimensions.Add (new ArrayDimension ()); + this.etype = MD.ElementType.Array; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ArrayType.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ArrayType.cs.meta new file mode 100644 index 0000000..bace606 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ArrayType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7add098db82a032428c139b59f0878be +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyDefinition.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyDefinition.cs new file mode 100644 index 0000000..de9ffde --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyDefinition.cs @@ -0,0 +1,189 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System; +using System.IO; +using System.Threading; + +namespace MonoFN.Cecil { + + public sealed class AssemblyDefinition : ICustomAttributeProvider, ISecurityDeclarationProvider, IDisposable { + + AssemblyNameDefinition name; + + internal ModuleDefinition main_module; + Collection modules; + Collection custom_attributes; + Collection security_declarations; + + public AssemblyNameDefinition Name { + get { return name; } + set { name = value; } + } + + public string FullName { + get { return name != null ? name.FullName : string.Empty; } + } + + public MetadataToken MetadataToken { + get { return new MetadataToken (TokenType.Assembly, 1); } + set { } + } + + public Collection Modules { + get { + if (modules != null) + return modules; + + if (main_module.HasImage) + return main_module.Read (ref modules, this, (_, reader) => reader.ReadModules ()); + + Interlocked.CompareExchange (ref modules, new Collection (1) { main_module }, null); + return modules; + } + } + + public ModuleDefinition MainModule { + get { return main_module; } + } + + public MethodDefinition EntryPoint { + get { return main_module.EntryPoint; } + set { main_module.EntryPoint = value; } + } + + public bool HasCustomAttributes { + get { + if (custom_attributes != null) + return custom_attributes.Count > 0; + + return this.GetHasCustomAttributes (main_module); + } + } + + public Collection CustomAttributes { + get { return custom_attributes ?? (this.GetCustomAttributes (ref custom_attributes, main_module)); } + } + + public bool HasSecurityDeclarations { + get { + if (security_declarations != null) + return security_declarations.Count > 0; + + return this.GetHasSecurityDeclarations (main_module); + } + } + + public Collection SecurityDeclarations { + get { return security_declarations ?? (this.GetSecurityDeclarations (ref security_declarations, main_module)); } + } + + internal AssemblyDefinition () + { + } + + public void Dispose () + { + if (this.modules == null) { + main_module.Dispose (); + return; + } + + var modules = this.Modules; + for (int i = 0; i < modules.Count; i++) + modules [i].Dispose (); + } + public static AssemblyDefinition CreateAssembly (AssemblyNameDefinition assemblyName, string moduleName, ModuleKind kind) + { + return CreateAssembly (assemblyName, moduleName, new ModuleParameters { Kind = kind }); + } + + public static AssemblyDefinition CreateAssembly (AssemblyNameDefinition assemblyName, string moduleName, ModuleParameters parameters) + { + if (assemblyName == null) + throw new ArgumentNullException ("assemblyName"); + if (moduleName == null) + throw new ArgumentNullException ("moduleName"); + Mixin.CheckParameters (parameters); + if (parameters.Kind == ModuleKind.NetModule) + throw new ArgumentException ("kind"); + + var assembly = ModuleDefinition.CreateModule (moduleName, parameters).Assembly; + assembly.Name = assemblyName; + + return assembly; + } + + public static AssemblyDefinition ReadAssembly (string fileName) + { + return ReadAssembly (ModuleDefinition.ReadModule (fileName)); + } + + public static AssemblyDefinition ReadAssembly (string fileName, ReaderParameters parameters) + { + return ReadAssembly (ModuleDefinition.ReadModule (fileName, parameters)); + } + + public static AssemblyDefinition ReadAssembly (Stream stream) + { + return ReadAssembly (ModuleDefinition.ReadModule (stream)); + } + + public static AssemblyDefinition ReadAssembly (Stream stream, ReaderParameters parameters) + { + return ReadAssembly (ModuleDefinition.ReadModule (stream, parameters)); + } + + static AssemblyDefinition ReadAssembly (ModuleDefinition module) + { + var assembly = module.Assembly; + if (assembly == null) + throw new ArgumentException (); + + return assembly; + } + + public void Write (string fileName) + { + Write (fileName, new WriterParameters ()); + } + + public void Write (string fileName, WriterParameters parameters) + { + main_module.Write (fileName, parameters); + } + + public void Write () + { + main_module.Write (); + } + + public void Write (WriterParameters parameters) + { + main_module.Write (parameters); + } + + public void Write (Stream stream) + { + Write (stream, new WriterParameters ()); + } + + public void Write (Stream stream, WriterParameters parameters) + { + main_module.Write (stream, parameters); + } + + public override string ToString () + { + return this.FullName; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyDefinition.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyDefinition.cs.meta new file mode 100644 index 0000000..d66f03d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyDefinition.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1c1ac0fc48f2d424f9c0d9fa74b16b07 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyFlags.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyFlags.cs new file mode 100644 index 0000000..a5f83a7 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyFlags.cs @@ -0,0 +1,24 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil { + + [Flags] + public enum AssemblyAttributes : uint { + PublicKey = 0x0001, + SideBySideCompatible = 0x0000, + Retargetable = 0x0100, + WindowsRuntime = 0x0200, + DisableJITCompileOptimizer = 0x4000, + EnableJITCompileTracking = 0x8000, + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyFlags.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyFlags.cs.meta new file mode 100644 index 0000000..d7c8791 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyFlags.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 257c8151138cda34dafbe0ca56ebedf4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyHashAlgorithm.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyHashAlgorithm.cs new file mode 100644 index 0000000..fb0cc5f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyHashAlgorithm.cs @@ -0,0 +1,22 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil { + + public enum AssemblyHashAlgorithm : uint { + None = 0x0000, + MD5 = 0x8003, + SHA1 = 0x8004, + SHA256 = 0x800C, + SHA384 = 0x800D, + SHA512 = 0x800E, + Reserved = 0x8003, // MD5 + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyHashAlgorithm.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyHashAlgorithm.cs.meta new file mode 100644 index 0000000..98687b5 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyHashAlgorithm.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b200bdec09584af41bd3f656e091cccf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyInfo.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyInfo.cs new file mode 100644 index 0000000..45534f1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyInfo.cs @@ -0,0 +1,22 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle (Consts.AssemblyName)] + +[assembly: Guid ("fd225bb4-fa53-44b2-a6db-85f5e48dcb54")] + +[assembly: InternalsVisibleTo ("MonoFN.Cecil.Tests, PublicKey=" + Consts.PublicKey)] +[assembly: InternalsVisibleTo ("MonoFN.Cecil.Pdb, PublicKey=" + Consts.PublicKey)] +[assembly: InternalsVisibleTo ("MonoFN.Cecil.Mdb, PublicKey=" + Consts.PublicKey)] +[assembly: InternalsVisibleTo ("MonoFN.Cecil.Rocks, PublicKey=" + Consts.PublicKey)] diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyInfo.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyInfo.cs.meta new file mode 100644 index 0000000..715025d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ff23d7231ddfa574b816532360874834 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyLinkedResource.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyLinkedResource.cs new file mode 100644 index 0000000..de592ae --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyLinkedResource.cs @@ -0,0 +1,37 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil { + + public sealed class AssemblyLinkedResource : Resource { + + AssemblyNameReference reference; + + public AssemblyNameReference Assembly { + get { return reference; } + set { reference = value; } + } + + public override ResourceType ResourceType { + get { return ResourceType.AssemblyLinked; } + } + + public AssemblyLinkedResource (string name, ManifestResourceAttributes flags) + : base (name, flags) + { + } + + public AssemblyLinkedResource (string name, ManifestResourceAttributes flags, AssemblyNameReference reference) + : base (name, flags) + { + this.reference = reference; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyLinkedResource.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyLinkedResource.cs.meta new file mode 100644 index 0000000..259e540 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyLinkedResource.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c3d5a207fc55ac5419e42c86cef15db0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyNameDefinition.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyNameDefinition.cs new file mode 100644 index 0000000..b46e1b9 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyNameDefinition.cs @@ -0,0 +1,32 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil { + + public sealed class AssemblyNameDefinition : AssemblyNameReference { + + public override byte [] Hash { + get { return Empty.Array; } + } + + internal AssemblyNameDefinition () + { + this.token = new MetadataToken (TokenType.Assembly, 1); + } + + public AssemblyNameDefinition (string name, Version version) + : base (name, version) + { + this.token = new MetadataToken (TokenType.Assembly, 1); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyNameDefinition.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyNameDefinition.cs.meta new file mode 100644 index 0000000..7409717 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyNameDefinition.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 68b90f4f023a67e4db7ddb52802ca21e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyNameReference.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyNameReference.cs new file mode 100644 index 0000000..d0b0585 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyNameReference.cs @@ -0,0 +1,269 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; +using System.Globalization; +using System.Security.Cryptography; +using System.Text; +using System.Threading; + +namespace MonoFN.Cecil { + + public class AssemblyNameReference : IMetadataScope { + + string name; + string culture; + Version version; + uint attributes; + byte [] public_key; + byte [] public_key_token; + AssemblyHashAlgorithm hash_algorithm; + byte [] hash; + + internal MetadataToken token; + + string full_name; + + public string Name { + get { return name; } + set { + name = value; + full_name = null; + } + } + + public string Culture { + get { return culture; } + set { + culture = value; + full_name = null; + } + } + + public Version Version { + get { return version; } + set { + version = Mixin.CheckVersion (value); + full_name = null; + } + } + + public AssemblyAttributes Attributes { + get { return (AssemblyAttributes)attributes; } + set { attributes = (uint)value; } + } + + public bool HasPublicKey { + get { return attributes.GetAttributes ((uint)AssemblyAttributes.PublicKey); } + set { attributes = attributes.SetAttributes ((uint)AssemblyAttributes.PublicKey, value); } + } + + public bool IsSideBySideCompatible { + get { return attributes.GetAttributes ((uint)AssemblyAttributes.SideBySideCompatible); } + set { attributes = attributes.SetAttributes ((uint)AssemblyAttributes.SideBySideCompatible, value); } + } + + public bool IsRetargetable { + get { return attributes.GetAttributes ((uint)AssemblyAttributes.Retargetable); } + set { attributes = attributes.SetAttributes ((uint)AssemblyAttributes.Retargetable, value); } + } + + public bool IsWindowsRuntime { + get { return attributes.GetAttributes ((uint)AssemblyAttributes.WindowsRuntime); } + set { attributes = attributes.SetAttributes ((uint)AssemblyAttributes.WindowsRuntime, value); } + } + + public byte [] PublicKey { + get { return public_key ?? Empty.Array; } + set { + public_key = value; + HasPublicKey = !public_key.IsNullOrEmpty (); + public_key_token = null; + full_name = null; + } + } + + public byte [] PublicKeyToken { + get { + if (public_key_token == null && !public_key.IsNullOrEmpty ()) { + var hash = HashPublicKey (); + // we need the last 8 bytes in reverse order + var local_public_key_token = new byte [8]; + Array.Copy (hash, (hash.Length - 8), local_public_key_token, 0, 8); + Array.Reverse (local_public_key_token, 0, 8); + Interlocked.CompareExchange (ref public_key_token, local_public_key_token, null); // publish only once finished (required for thread-safety) + } + return public_key_token ?? Empty.Array; + } + set { + public_key_token = value; + full_name = null; + } + } + + byte [] HashPublicKey () + { + HashAlgorithm algorithm; + + switch (hash_algorithm) { + case AssemblyHashAlgorithm.Reserved: + algorithm = MD5.Create (); + break; + default: + // None default to SHA1 + algorithm = SHA1.Create (); + break; + } + + using (algorithm) + return algorithm.ComputeHash (public_key); + } + + public virtual MetadataScopeType MetadataScopeType { + get { return MetadataScopeType.AssemblyNameReference; } + } + + public string FullName { + get { + if (full_name != null) + return full_name; + + const string sep = ", "; + + var builder = new StringBuilder (); + builder.Append (name); + builder.Append (sep); + builder.Append ("Version="); + builder.Append (version.ToString (fieldCount: 4)); + builder.Append (sep); + builder.Append ("Culture="); + builder.Append (string.IsNullOrEmpty (culture) ? "neutral" : culture); + builder.Append (sep); + builder.Append ("PublicKeyToken="); + + var pk_token = PublicKeyToken; + if (!pk_token.IsNullOrEmpty () && pk_token.Length > 0) { + for (int i = 0; i < pk_token.Length; i++) { + builder.Append (pk_token [i].ToString ("x2")); + } + } else + builder.Append ("null"); + + if (IsRetargetable) { + builder.Append (sep); + builder.Append ("Retargetable=Yes"); + } + + Interlocked.CompareExchange (ref full_name, builder.ToString (), null); + + return full_name; + } + } + + public static AssemblyNameReference Parse (string fullName) + { + if (fullName == null) + throw new ArgumentNullException ("fullName"); + if (fullName.Length == 0) + throw new ArgumentException ("Name can not be empty"); + + var name = new AssemblyNameReference (); + var tokens = fullName.Split (','); + for (int i = 0; i < tokens.Length; i++) { + var token = tokens [i].Trim (); + + if (i == 0) { + name.Name = token; + continue; + } + + var parts = token.Split ('='); + if (parts.Length != 2) + throw new ArgumentException ("Malformed name"); + + switch (parts [0].ToLowerInvariant ()) { + case "version": + name.Version = new Version (parts [1]); + break; + case "culture": + name.Culture = parts [1] == "neutral" ? "" : parts [1]; + break; + case "publickeytoken": + var pk_token = parts [1]; + if (pk_token == "null") + break; + + name.PublicKeyToken = new byte [pk_token.Length / 2]; + for (int j = 0; j < name.PublicKeyToken.Length; j++) + name.PublicKeyToken [j] = Byte.Parse (pk_token.Substring (j * 2, 2), NumberStyles.HexNumber); + + break; + } + } + + return name; + } + + public AssemblyHashAlgorithm HashAlgorithm { + get { return hash_algorithm; } + set { hash_algorithm = value; } + } + + public virtual byte [] Hash { + get { return hash; } + set { hash = value; } + } + + public MetadataToken MetadataToken { + get { return token; } + set { token = value; } + } + + internal AssemblyNameReference () + { + this.version = Mixin.ZeroVersion; + this.token = new MetadataToken (TokenType.AssemblyRef); + } + + public AssemblyNameReference (string name, Version version) + { + Mixin.CheckName (name); + + this.name = name; + this.version = Mixin.CheckVersion (version); + this.hash_algorithm = AssemblyHashAlgorithm.None; + this.token = new MetadataToken (TokenType.AssemblyRef); + } + + public override string ToString () + { + return this.FullName; + } + } + + partial class Mixin { + + public static Version ZeroVersion = new Version (0, 0, 0, 0); + + public static Version CheckVersion (Version version) + { + if (version == null) + return ZeroVersion; + + if (version.Build == -1) + return new Version (version.Major, version.Minor, 0, 0); + + if (version.Revision == -1) + return new Version (version.Major, version.Minor, version.Build, 0); + + return version; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyNameReference.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyNameReference.cs.meta new file mode 100644 index 0000000..980b1fd --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyNameReference.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fdaf7563058f3f54ba3e6b32c888eb3c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyReader.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyReader.cs new file mode 100644 index 0000000..2a3d6ef --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyReader.cs @@ -0,0 +1,3889 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.Cil; +using MonoFN.Cecil.Metadata; +using MonoFN.Cecil.PE; +using MonoFN.Collections.Generic; +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Text; +using RVA = System.UInt32; + +namespace MonoFN.Cecil { + + abstract class ModuleReader { + + readonly protected ModuleDefinition module; + + protected ModuleReader (Image image, ReadingMode mode) + { + this.module = new ModuleDefinition (image); + this.module.ReadingMode = mode; + } + + protected abstract void ReadModule (); + public abstract void ReadSymbols (ModuleDefinition module); + + protected void ReadModuleManifest (MetadataReader reader) + { + reader.Populate (module); + + ReadAssembly (reader); + } + + void ReadAssembly (MetadataReader reader) + { + var name = reader.ReadAssemblyNameDefinition (); + if (name == null) { + module.kind = ModuleKind.NetModule; + return; + } + + var assembly = new AssemblyDefinition (); + assembly.Name = name; + + module.assembly = assembly; + assembly.main_module = module; + } + + public static ModuleDefinition CreateModule (Image image, ReaderParameters parameters) + { + var reader = CreateModuleReader (image, parameters.ReadingMode); + var module = reader.module; + + if (parameters.assembly_resolver != null) + module.assembly_resolver = Disposable.NotOwned (parameters.assembly_resolver); + + if (parameters.metadata_resolver != null) + module.metadata_resolver = parameters.metadata_resolver; + + if (parameters.metadata_importer_provider != null) + module.metadata_importer = parameters.metadata_importer_provider.GetMetadataImporter (module); + + if (parameters.reflection_importer_provider != null) + module.reflection_importer = parameters.reflection_importer_provider.GetReflectionImporter (module); + + GetMetadataKind (module, parameters); + + reader.ReadModule (); + + ReadSymbols (module, parameters); + + reader.ReadSymbols (module); + + if (parameters.ReadingMode == ReadingMode.Immediate) + module.MetadataSystem.Clear (); + + return module; + } + + static void ReadSymbols (ModuleDefinition module, ReaderParameters parameters) + { + var symbol_reader_provider = parameters.SymbolReaderProvider; + + if (symbol_reader_provider == null && parameters.ReadSymbols) + symbol_reader_provider = new DefaultSymbolReaderProvider (); + + if (symbol_reader_provider != null) { + module.SymbolReaderProvider = symbol_reader_provider; + + var reader = parameters.SymbolStream != null + ? symbol_reader_provider.GetSymbolReader (module, parameters.SymbolStream) + : symbol_reader_provider.GetSymbolReader (module, module.FileName); + + if (reader != null) { + try { + module.ReadSymbols (reader, parameters.ThrowIfSymbolsAreNotMatching); + } + catch (Exception) { + reader.Dispose (); + throw; + } + } + } + + if (module.Image.HasDebugTables ()) + module.ReadSymbols (new PortablePdbReader (module.Image, module)); + } + + static void GetMetadataKind (ModuleDefinition module, ReaderParameters parameters) + { + if (!parameters.ApplyWindowsRuntimeProjections) { + module.MetadataKind = MetadataKind.Ecma335; + return; + } + + var runtime_version = module.RuntimeVersion; + + if (!runtime_version.Contains ("WindowsRuntime")) + module.MetadataKind = MetadataKind.Ecma335; + else if (runtime_version.Contains ("CLR")) + module.MetadataKind = MetadataKind.ManagedWindowsMetadata; + else + module.MetadataKind = MetadataKind.WindowsMetadata; + } + + static ModuleReader CreateModuleReader (Image image, ReadingMode mode) + { + switch (mode) { + case ReadingMode.Immediate: + return new ImmediateModuleReader (image); + case ReadingMode.Deferred: + return new DeferredModuleReader (image); + default: + throw new ArgumentException (); + } + } + } + + sealed class ImmediateModuleReader : ModuleReader { + + bool resolve_attributes; + + public ImmediateModuleReader (Image image) + : base (image, ReadingMode.Immediate) + { + } + + protected override void ReadModule () + { + this.module.Read (this.module, (module, reader) => { + ReadModuleManifest (reader); + ReadModule (module, resolve_attributes: true); + }); + } + + public void ReadModule (ModuleDefinition module, bool resolve_attributes) + { + this.resolve_attributes = resolve_attributes; + + if (module.HasAssemblyReferences) + Mixin.Read (module.AssemblyReferences); + if (module.HasResources) + Mixin.Read (module.Resources); + if (module.HasModuleReferences) + Mixin.Read (module.ModuleReferences); + if (module.HasTypes) + ReadTypes (module.Types); + if (module.HasExportedTypes) + Mixin.Read (module.ExportedTypes); + + ReadCustomAttributes (module); + + var assembly = module.Assembly; + if (module.kind == ModuleKind.NetModule || assembly == null) + return; + + ReadCustomAttributes (assembly); + ReadSecurityDeclarations (assembly); + } + + void ReadTypes (Collection types) + { + for (int i = 0; i < types.Count; i++) + ReadType (types [i]); + } + + void ReadType (TypeDefinition type) + { + ReadGenericParameters (type); + + if (type.HasInterfaces) + ReadInterfaces (type); + + if (type.HasNestedTypes) + ReadTypes (type.NestedTypes); + + if (type.HasLayoutInfo) + Mixin.Read (type.ClassSize); + + if (type.HasFields) + ReadFields (type); + + if (type.HasMethods) + ReadMethods (type); + + if (type.HasProperties) + ReadProperties (type); + + if (type.HasEvents) + ReadEvents (type); + + ReadSecurityDeclarations (type); + ReadCustomAttributes (type); + } + + void ReadInterfaces (TypeDefinition type) + { + var interfaces = type.Interfaces; + + for (int i = 0; i < interfaces.Count; i++) + ReadCustomAttributes (interfaces [i]); + } + + void ReadGenericParameters (IGenericParameterProvider provider) + { + if (!provider.HasGenericParameters) + return; + + var parameters = provider.GenericParameters; + + for (int i = 0; i < parameters.Count; i++) { + var parameter = parameters [i]; + + if (parameter.HasConstraints) + ReadGenericParameterConstraints (parameter); + + ReadCustomAttributes (parameter); + } + } + + void ReadGenericParameterConstraints (GenericParameter parameter) + { + var constraints = parameter.Constraints; + + for (int i = 0; i < constraints.Count; i++) + ReadCustomAttributes (constraints [i]); + } + + void ReadSecurityDeclarations (ISecurityDeclarationProvider provider) + { + if (!provider.HasSecurityDeclarations) + return; + + var security_declarations = provider.SecurityDeclarations; + + if (!resolve_attributes) + return; + + for (int i = 0; i < security_declarations.Count; i++) { + var security_declaration = security_declarations [i]; + + Mixin.Read (security_declaration.SecurityAttributes); + } + } + + void ReadCustomAttributes (ICustomAttributeProvider provider) + { + if (!provider.HasCustomAttributes) + return; + + var custom_attributes = provider.CustomAttributes; + + if (!resolve_attributes) + return; + + for (int i = 0; i < custom_attributes.Count; i++) { + var custom_attribute = custom_attributes [i]; + + Mixin.Read (custom_attribute.ConstructorArguments); + } + } + + void ReadFields (TypeDefinition type) + { + var fields = type.Fields; + + for (int i = 0; i < fields.Count; i++) { + var field = fields [i]; + + if (field.HasConstant) + Mixin.Read (field.Constant); + + if (field.HasLayoutInfo) + Mixin.Read (field.Offset); + + if (field.RVA > 0) + Mixin.Read (field.InitialValue); + + if (field.HasMarshalInfo) + Mixin.Read (field.MarshalInfo); + + ReadCustomAttributes (field); + } + } + + void ReadMethods (TypeDefinition type) + { + var methods = type.Methods; + + for (int i = 0; i < methods.Count; i++) { + var method = methods [i]; + + ReadGenericParameters (method); + + if (method.HasParameters) + ReadParameters (method); + + if (method.HasOverrides) + Mixin.Read (method.Overrides); + + if (method.IsPInvokeImpl) + Mixin.Read (method.PInvokeInfo); + + ReadSecurityDeclarations (method); + ReadCustomAttributes (method); + + var return_type = method.MethodReturnType; + if (return_type.HasConstant) + Mixin.Read (return_type.Constant); + + if (return_type.HasMarshalInfo) + Mixin.Read (return_type.MarshalInfo); + + ReadCustomAttributes (return_type); + } + } + + void ReadParameters (MethodDefinition method) + { + var parameters = method.Parameters; + + for (int i = 0; i < parameters.Count; i++) { + var parameter = parameters [i]; + + if (parameter.HasConstant) + Mixin.Read (parameter.Constant); + + if (parameter.HasMarshalInfo) + Mixin.Read (parameter.MarshalInfo); + + ReadCustomAttributes (parameter); + } + } + + void ReadProperties (TypeDefinition type) + { + var properties = type.Properties; + + for (int i = 0; i < properties.Count; i++) { + var property = properties [i]; + + Mixin.Read (property.GetMethod); + + if (property.HasConstant) + Mixin.Read (property.Constant); + + ReadCustomAttributes (property); + } + } + + void ReadEvents (TypeDefinition type) + { + var events = type.Events; + + for (int i = 0; i < events.Count; i++) { + var @event = events [i]; + + Mixin.Read (@event.AddMethod); + + ReadCustomAttributes (@event); + } + } + + public override void ReadSymbols (ModuleDefinition module) + { + if (module.symbol_reader == null) + return; + + ReadTypesSymbols (module.Types, module.symbol_reader); + } + + void ReadTypesSymbols (Collection types, ISymbolReader symbol_reader) + { + for (int i = 0; i < types.Count; i++) { + var type = types [i]; + + if (type.HasNestedTypes) + ReadTypesSymbols (type.NestedTypes, symbol_reader); + + if (type.HasMethods) + ReadMethodsSymbols (type, symbol_reader); + } + } + + void ReadMethodsSymbols (TypeDefinition type, ISymbolReader symbol_reader) + { + var methods = type.Methods; + for (int i = 0; i < methods.Count; i++) { + var method = methods [i]; + + if (method.HasBody && method.token.RID != 0 && method.debug_info == null) + method.debug_info = symbol_reader.Read (method); + } + } + } + + sealed class DeferredModuleReader : ModuleReader { + + public DeferredModuleReader (Image image) + : base (image, ReadingMode.Deferred) + { + } + + protected override void ReadModule () + { + this.module.Read (this.module, (_, reader) => ReadModuleManifest (reader)); + } + + public override void ReadSymbols (ModuleDefinition module) + { + } + } + + sealed class MetadataReader : ByteBuffer { + + readonly internal Image image; + readonly internal ModuleDefinition module; + readonly internal MetadataSystem metadata; + + internal CodeReader code; + internal IGenericContext context; + + readonly MetadataReader metadata_reader; + + public MetadataReader (ModuleDefinition module) + : base (module.Image.TableHeap.data) + { + this.image = module.Image; + this.module = module; + this.metadata = module.MetadataSystem; + this.code = new CodeReader (this); + } + + public MetadataReader (Image image, ModuleDefinition module, MetadataReader metadata_reader) + : base (image.TableHeap.data) + { + this.image = image; + this.module = module; + this.metadata = module.MetadataSystem; + this.metadata_reader = metadata_reader; + } + + int GetCodedIndexSize (CodedIndex index) + { + return image.GetCodedIndexSize (index); + } + + uint ReadByIndexSize (int size) + { + if (size == 4) + return ReadUInt32 (); + else + return ReadUInt16 (); + } + + byte [] ReadBlob () + { + var blob_heap = image.BlobHeap; + if (blob_heap == null) { + position += 2; + return Empty.Array; + } + + return blob_heap.Read (ReadBlobIndex ()); + } + + byte [] ReadBlob (uint signature) + { + var blob_heap = image.BlobHeap; + if (blob_heap == null) + return Empty.Array; + + return blob_heap.Read (signature); + } + + uint ReadBlobIndex () + { + var blob_heap = image.BlobHeap; + return ReadByIndexSize (blob_heap != null ? blob_heap.IndexSize : 2); + } + + void GetBlobView (uint signature, out byte [] blob, out int index, out int count) + { + var blob_heap = image.BlobHeap; + if (blob_heap == null) { + blob = null; + index = count = 0; + return; + } + + blob_heap.GetView (signature, out blob, out index, out count); + } + + string ReadString () + { + return image.StringHeap.Read (ReadByIndexSize (image.StringHeap.IndexSize)); + } + + uint ReadStringIndex () + { + return ReadByIndexSize (image.StringHeap.IndexSize); + } + + Guid ReadGuid () + { + return image.GuidHeap.Read (ReadByIndexSize (image.GuidHeap.IndexSize)); + } + + uint ReadTableIndex (Table table) + { + return ReadByIndexSize (image.GetTableIndexSize (table)); + } + + MetadataToken ReadMetadataToken (CodedIndex index) + { + return index.GetMetadataToken (ReadByIndexSize (GetCodedIndexSize (index))); + } + + int MoveTo (Table table) + { + var info = image.TableHeap [table]; + if (info.Length != 0) + this.position = (int)info.Offset; + + return (int)info.Length; + } + + bool MoveTo (Table table, uint row) + { + var info = image.TableHeap [table]; + var length = info.Length; + if (length == 0 || row > length) + return false; + + this.position = (int)(info.Offset + (info.RowSize * (row - 1))); + return true; + } + + public AssemblyNameDefinition ReadAssemblyNameDefinition () + { + if (MoveTo (Table.Assembly) == 0) + return null; + + var name = new AssemblyNameDefinition (); + + name.HashAlgorithm = (AssemblyHashAlgorithm)ReadUInt32 (); + + PopulateVersionAndFlags (name); + + name.PublicKey = ReadBlob (); + + PopulateNameAndCulture (name); + + return name; + } + + public ModuleDefinition Populate (ModuleDefinition module) + { + if (MoveTo (Table.Module) == 0) + return module; + + Advance (2); // Generation + + module.Name = ReadString (); + module.Mvid = ReadGuid (); + + return module; + } + + void InitializeAssemblyReferences () + { + if (metadata.AssemblyReferences != null) + return; + + int length = MoveTo (Table.AssemblyRef); + var references = metadata.AssemblyReferences = new AssemblyNameReference [length]; + + for (uint i = 0; i < length; i++) { + var reference = new AssemblyNameReference (); + reference.token = new MetadataToken (TokenType.AssemblyRef, i + 1); + + PopulateVersionAndFlags (reference); + + var key_or_token = ReadBlob (); + + if (reference.HasPublicKey) + reference.PublicKey = key_or_token; + else + reference.PublicKeyToken = key_or_token; + + PopulateNameAndCulture (reference); + + reference.Hash = ReadBlob (); + + references [i] = reference; + } + } + + public Collection ReadAssemblyReferences () + { + InitializeAssemblyReferences (); + + var references = new Collection (metadata.AssemblyReferences); + if (module.IsWindowsMetadata ()) + module.Projections.AddVirtualReferences (references); + + return references; + } + + public MethodDefinition ReadEntryPoint () + { + if (module.Image.EntryPointToken == 0) + return null; + + var token = new MetadataToken (module.Image.EntryPointToken); + return GetMethodDefinition (token.RID); + } + + public Collection ReadModules () + { + var modules = new Collection (1); + modules.Add (this.module); + + int length = MoveTo (Table.File); + for (uint i = 1; i <= length; i++) { + var attributes = (FileAttributes)ReadUInt32 (); + var name = ReadString (); + ReadBlobIndex (); + + if (attributes != FileAttributes.ContainsMetaData) + continue; + + var parameters = new ReaderParameters { + ReadingMode = module.ReadingMode, + SymbolReaderProvider = module.SymbolReaderProvider, + AssemblyResolver = module.AssemblyResolver + }; + + var netmodule = ModuleDefinition.ReadModule (GetModuleFileName (name), parameters); + netmodule.assembly = this.module.assembly; + + modules.Add (netmodule); + } + + return modules; + } + + string GetModuleFileName (string name) + { + if (module.FileName == null) + throw new NotSupportedException (); + + var path = Path.GetDirectoryName (module.FileName); + return Path.Combine (path, name); + } + + void InitializeModuleReferences () + { + if (metadata.ModuleReferences != null) + return; + + int length = MoveTo (Table.ModuleRef); + var references = metadata.ModuleReferences = new ModuleReference [length]; + + for (uint i = 0; i < length; i++) { + var reference = new ModuleReference (ReadString ()); + reference.token = new MetadataToken (TokenType.ModuleRef, i + 1); + + references [i] = reference; + } + } + + public Collection ReadModuleReferences () + { + InitializeModuleReferences (); + + return new Collection (metadata.ModuleReferences); + } + + public bool HasFileResource () + { + int length = MoveTo (Table.File); + if (length == 0) + return false; + + for (uint i = 1; i <= length; i++) + if (ReadFileRecord (i).Col1 == FileAttributes.ContainsNoMetaData) + return true; + + return false; + } + + public Collection ReadResources () + { + int length = MoveTo (Table.ManifestResource); + var resources = new Collection (length); + + for (int i = 1; i <= length; i++) { + var offset = ReadUInt32 (); + var flags = (ManifestResourceAttributes)ReadUInt32 (); + var name = ReadString (); + var implementation = ReadMetadataToken (CodedIndex.Implementation); + + Resource resource; + + if (implementation.RID == 0) { + resource = new EmbeddedResource (name, flags, offset, this); + } else if (implementation.TokenType == TokenType.AssemblyRef) { + resource = new AssemblyLinkedResource (name, flags) { + Assembly = (AssemblyNameReference)GetTypeReferenceScope (implementation), + }; + } else if (implementation.TokenType == TokenType.File) { + var file_record = ReadFileRecord (implementation.RID); + + resource = new LinkedResource (name, flags) { + File = file_record.Col2, + hash = ReadBlob (file_record.Col3) + }; + } else + continue; + + resources.Add (resource); + } + + return resources; + } + + Row ReadFileRecord (uint rid) + { + var position = this.position; + + if (!MoveTo (Table.File, rid)) + throw new ArgumentException (); + + var record = new Row ( + (FileAttributes)ReadUInt32 (), + ReadString (), + ReadBlobIndex ()); + + this.position = position; + + return record; + } + + public byte [] GetManagedResource (uint offset) + { + return image.GetReaderAt (image.Resources.VirtualAddress, offset, (o, reader) => { + reader.Advance ((int)o); + return reader.ReadBytes (reader.ReadInt32 ()); + }) ?? Empty.Array; + } + + void PopulateVersionAndFlags (AssemblyNameReference name) + { + name.Version = new Version ( + ReadUInt16 (), + ReadUInt16 (), + ReadUInt16 (), + ReadUInt16 ()); + + name.Attributes = (AssemblyAttributes)ReadUInt32 (); + } + + void PopulateNameAndCulture (AssemblyNameReference name) + { + name.Name = ReadString (); + name.Culture = ReadString (); + } + + public TypeDefinitionCollection ReadTypes () + { + InitializeTypeDefinitions (); + var mtypes = metadata.Types; + var type_count = mtypes.Length - metadata.NestedTypes.Count; + var types = new TypeDefinitionCollection (module, type_count); + + for (int i = 0; i < mtypes.Length; i++) { + var type = mtypes [i]; + if (IsNested (type.Attributes)) + continue; + + types.Add (type); + } + + if (image.HasTable (Table.MethodPtr) || image.HasTable (Table.FieldPtr)) + CompleteTypes (); + + return types; + } + + void CompleteTypes () + { + var types = metadata.Types; + + for (int i = 0; i < types.Length; i++) { + var type = types [i]; + + Mixin.Read (type.Fields); + Mixin.Read (type.Methods); + } + } + + void InitializeTypeDefinitions () + { + if (metadata.Types != null) + return; + + InitializeNestedTypes (); + InitializeFields (); + InitializeMethods (); + + int length = MoveTo (Table.TypeDef); + var types = metadata.Types = new TypeDefinition [length]; + + for (uint i = 0; i < length; i++) { + if (types [i] != null) + continue; + + types [i] = ReadType (i + 1); + } + + if (module.IsWindowsMetadata ()) { + for (uint i = 0; i < length; i++) { + WindowsRuntimeProjections.Project (types [i]); + } + } + } + + static bool IsNested (TypeAttributes attributes) + { + switch (attributes & TypeAttributes.VisibilityMask) { + case TypeAttributes.NestedAssembly: + case TypeAttributes.NestedFamANDAssem: + case TypeAttributes.NestedFamily: + case TypeAttributes.NestedFamORAssem: + case TypeAttributes.NestedPrivate: + case TypeAttributes.NestedPublic: + return true; + default: + return false; + } + } + + public bool HasNestedTypes (TypeDefinition type) + { + Collection mapping; + InitializeNestedTypes (); + + if (!metadata.TryGetNestedTypeMapping (type, out mapping)) + return false; + + return mapping.Count > 0; + } + + public Collection ReadNestedTypes (TypeDefinition type) + { + InitializeNestedTypes (); + Collection mapping; + if (!metadata.TryGetNestedTypeMapping (type, out mapping)) + return new MemberDefinitionCollection (type); + + var nested_types = new MemberDefinitionCollection (type, mapping.Count); + + for (int i = 0; i < mapping.Count; i++) { + var nested_type = GetTypeDefinition (mapping [i]); + + if (nested_type != null) + nested_types.Add (nested_type); + } + + metadata.RemoveNestedTypeMapping (type); + + return nested_types; + } + + void InitializeNestedTypes () + { + if (metadata.NestedTypes != null) + return; + + var length = MoveTo (Table.NestedClass); + + metadata.NestedTypes = new Dictionary> (length); + metadata.ReverseNestedTypes = new Dictionary (length); + + if (length == 0) + return; + + for (int i = 1; i <= length; i++) { + var nested = ReadTableIndex (Table.TypeDef); + var declaring = ReadTableIndex (Table.TypeDef); + + AddNestedMapping (declaring, nested); + } + } + + void AddNestedMapping (uint declaring, uint nested) + { + metadata.SetNestedTypeMapping (declaring, AddMapping (metadata.NestedTypes, declaring, nested)); + metadata.SetReverseNestedTypeMapping (nested, declaring); + } + + static Collection AddMapping (Dictionary> cache, TKey key, TValue value) + { + Collection mapped; + if (!cache.TryGetValue (key, out mapped)) { + mapped = new Collection (); + } + mapped.Add (value); + return mapped; + } + + TypeDefinition ReadType (uint rid) + { + if (!MoveTo (Table.TypeDef, rid)) + return null; + + var attributes = (TypeAttributes)ReadUInt32 (); + var name = ReadString (); + var @namespace = ReadString (); + var type = new TypeDefinition (@namespace, name, attributes); + type.token = new MetadataToken (TokenType.TypeDef, rid); + type.scope = module; + type.module = module; + + metadata.AddTypeDefinition (type); + + this.context = type; + + type.BaseType = GetTypeDefOrRef (ReadMetadataToken (CodedIndex.TypeDefOrRef)); + + type.fields_range = ReadListRange (rid, Table.TypeDef, Table.Field); + type.methods_range = ReadListRange (rid, Table.TypeDef, Table.Method); + + if (IsNested (attributes)) + type.DeclaringType = GetNestedTypeDeclaringType (type); + + return type; + } + + TypeDefinition GetNestedTypeDeclaringType (TypeDefinition type) + { + uint declaring_rid; + if (!metadata.TryGetReverseNestedTypeMapping (type, out declaring_rid)) + return null; + + metadata.RemoveReverseNestedTypeMapping (type); + return GetTypeDefinition (declaring_rid); + } + + Range ReadListRange (uint current_index, Table current, Table target) + { + var list = new Range (); + + var start = ReadTableIndex (target); + if (start == 0) + return list; + + uint next_index; + var current_table = image.TableHeap [current]; + + if (current_index == current_table.Length) + next_index = image.TableHeap [target].Length + 1; + else { + var position = this.position; + this.position += (int)(current_table.RowSize - image.GetTableIndexSize (target)); + next_index = ReadTableIndex (target); + this.position = position; + } + + list.Start = start; + list.Length = next_index - start; + + return list; + } + + public Row ReadTypeLayout (TypeDefinition type) + { + InitializeTypeLayouts (); + Row class_layout; + var rid = type.token.RID; + if (!metadata.ClassLayouts.TryGetValue (rid, out class_layout)) + return new Row (Mixin.NoDataMarker, Mixin.NoDataMarker); + + type.PackingSize = (short)class_layout.Col1; + type.ClassSize = (int)class_layout.Col2; + + metadata.ClassLayouts.Remove (rid); + + return new Row ((short)class_layout.Col1, (int)class_layout.Col2); + } + + void InitializeTypeLayouts () + { + if (metadata.ClassLayouts != null) + return; + + int length = MoveTo (Table.ClassLayout); + + var class_layouts = metadata.ClassLayouts = new Dictionary> (length); + + for (uint i = 0; i < length; i++) { + var packing_size = ReadUInt16 (); + var class_size = ReadUInt32 (); + + var parent = ReadTableIndex (Table.TypeDef); + + class_layouts.Add (parent, new Row (packing_size, class_size)); + } + } + + public TypeReference GetTypeDefOrRef (MetadataToken token) + { + return (TypeReference)LookupToken (token); + } + + public TypeDefinition GetTypeDefinition (uint rid) + { + InitializeTypeDefinitions (); + + var type = metadata.GetTypeDefinition (rid); + if (type != null) + return type; + + type = ReadTypeDefinition (rid); + + if (module.IsWindowsMetadata ()) + WindowsRuntimeProjections.Project (type); + + return type; + } + + TypeDefinition ReadTypeDefinition (uint rid) + { + if (!MoveTo (Table.TypeDef, rid)) + return null; + + return ReadType (rid); + } + + void InitializeTypeReferences () + { + if (metadata.TypeReferences != null) + return; + + metadata.TypeReferences = new TypeReference [image.GetTableLength (Table.TypeRef)]; + } + + public TypeReference GetTypeReference (string scope, string full_name) + { + InitializeTypeReferences (); + + var length = metadata.TypeReferences.Length; + + for (uint i = 1; i <= length; i++) { + var type = GetTypeReference (i); + + if (type.FullName != full_name) + continue; + + if (string.IsNullOrEmpty (scope)) + return type; + + if (type.Scope.Name == scope) + return type; + } + + return null; + } + + TypeReference GetTypeReference (uint rid) + { + InitializeTypeReferences (); + + var type = metadata.GetTypeReference (rid); + if (type != null) + return type; + + return ReadTypeReference (rid); + } + + TypeReference ReadTypeReference (uint rid) + { + if (!MoveTo (Table.TypeRef, rid)) + return null; + + TypeReference declaring_type = null; + IMetadataScope scope; + + var scope_token = ReadMetadataToken (CodedIndex.ResolutionScope); + + var name = ReadString (); + var @namespace = ReadString (); + + var type = new TypeReference ( + @namespace, + name, + module, + null); + + type.token = new MetadataToken (TokenType.TypeRef, rid); + + metadata.AddTypeReference (type); + + if (scope_token.TokenType == TokenType.TypeRef) { + if (scope_token.RID != rid) { + declaring_type = GetTypeDefOrRef (scope_token); + + scope = declaring_type != null + ? declaring_type.Scope + : module; + } else // obfuscated typeref row pointing to self + scope = module; + } else + scope = GetTypeReferenceScope (scope_token); + + type.scope = scope; + type.DeclaringType = declaring_type; + + MetadataSystem.TryProcessPrimitiveTypeReference (type); + + if (type.Module.IsWindowsMetadata ()) + WindowsRuntimeProjections.Project (type); + + return type; + } + + IMetadataScope GetTypeReferenceScope (MetadataToken scope) + { + if (scope.TokenType == TokenType.Module) + return module; + + IMetadataScope [] scopes; + + switch (scope.TokenType) { + case TokenType.AssemblyRef: + InitializeAssemblyReferences (); + scopes = metadata.AssemblyReferences; + break; + case TokenType.ModuleRef: + InitializeModuleReferences (); + scopes = metadata.ModuleReferences; + break; + default: + throw new NotSupportedException (); + } + + var index = scope.RID - 1; + if (index < 0 || index >= scopes.Length) + return null; + + return scopes [index]; + } + + public IEnumerable GetTypeReferences () + { + InitializeTypeReferences (); + + var length = image.GetTableLength (Table.TypeRef); + + var type_references = new TypeReference [length]; + + for (uint i = 1; i <= length; i++) + type_references [i - 1] = GetTypeReference (i); + + return type_references; + } + + TypeReference GetTypeSpecification (uint rid) + { + if (!MoveTo (Table.TypeSpec, rid)) + return null; + + var reader = ReadSignature (ReadBlobIndex ()); + var type = reader.ReadTypeSignature (); + if (type.token.RID == 0) + type.token = new MetadataToken (TokenType.TypeSpec, rid); + + return type; + } + + SignatureReader ReadSignature (uint signature) + { + return new SignatureReader (signature, this); + } + + public bool HasInterfaces (TypeDefinition type) + { + InitializeInterfaces (); + Collection> mapping; + + return metadata.TryGetInterfaceMapping (type, out mapping); + } + + public InterfaceImplementationCollection ReadInterfaces (TypeDefinition type) + { + InitializeInterfaces (); + Collection> mapping; + + if (!metadata.TryGetInterfaceMapping (type, out mapping)) + return new InterfaceImplementationCollection (type); + + var interfaces = new InterfaceImplementationCollection (type, mapping.Count); + + this.context = type; + + for (int i = 0; i < mapping.Count; i++) { + interfaces.Add ( + new InterfaceImplementation ( + GetTypeDefOrRef (mapping [i].Col2), + new MetadataToken (TokenType.InterfaceImpl, mapping [i].Col1))); + } + + metadata.RemoveInterfaceMapping (type); + + return interfaces; + } + + void InitializeInterfaces () + { + if (metadata.Interfaces != null) + return; + + int length = MoveTo (Table.InterfaceImpl); + + metadata.Interfaces = new Dictionary>> (length); + + for (uint i = 1; i <= length; i++) { + var type = ReadTableIndex (Table.TypeDef); + var @interface = ReadMetadataToken (CodedIndex.TypeDefOrRef); + + AddInterfaceMapping (type, new Row (i, @interface)); + } + } + + void AddInterfaceMapping (uint type, Row @interface) + { + metadata.SetInterfaceMapping (type, AddMapping (metadata.Interfaces, type, @interface)); + } + + public Collection ReadFields (TypeDefinition type) + { + var fields_range = type.fields_range; + if (fields_range.Length == 0) + return new MemberDefinitionCollection (type); + + var fields = new MemberDefinitionCollection (type, (int)fields_range.Length); + this.context = type; + + if (!MoveTo (Table.FieldPtr, fields_range.Start)) { + if (!MoveTo (Table.Field, fields_range.Start)) + return fields; + + for (uint i = 0; i < fields_range.Length; i++) + ReadField (fields_range.Start + i, fields); + } else + ReadPointers (Table.FieldPtr, Table.Field, fields_range, fields, ReadField); + + return fields; + } + + void ReadField (uint field_rid, Collection fields) + { + var attributes = (FieldAttributes)ReadUInt16 (); + var name = ReadString (); + var signature = ReadBlobIndex (); + + var field = new FieldDefinition (name, attributes, ReadFieldType (signature)); + field.token = new MetadataToken (TokenType.Field, field_rid); + metadata.AddFieldDefinition (field); + + if (IsDeleted (field)) + return; + + fields.Add (field); + + if (module.IsWindowsMetadata ()) + WindowsRuntimeProjections.Project (field); + } + + void InitializeFields () + { + if (metadata.Fields != null) + return; + + metadata.Fields = new FieldDefinition [image.GetTableLength (Table.Field)]; + } + + TypeReference ReadFieldType (uint signature) + { + var reader = ReadSignature (signature); + + const byte field_sig = 0x6; + + if (reader.ReadByte () != field_sig) + throw new NotSupportedException (); + + return reader.ReadTypeSignature (); + } + + public int ReadFieldRVA (FieldDefinition field) + { + InitializeFieldRVAs (); + var rid = field.token.RID; + + RVA rva; + if (!metadata.FieldRVAs.TryGetValue (rid, out rva)) + return 0; + + var size = GetFieldTypeSize (field.FieldType); + + if (size == 0 || rva == 0) + return 0; + + metadata.FieldRVAs.Remove (rid); + + field.InitialValue = GetFieldInitializeValue (size, rva); + + return (int)rva; + } + + byte [] GetFieldInitializeValue (int size, RVA rva) + { + return image.GetReaderAt (rva, size, (s, reader) => reader.ReadBytes (s)) ?? Empty.Array; + } + + static int GetFieldTypeSize (TypeReference type) + { + int size = 0; + + switch (type.etype) { + case ElementType.Boolean: + case ElementType.U1: + case ElementType.I1: + size = 1; + break; + case ElementType.U2: + case ElementType.I2: + case ElementType.Char: + size = 2; + break; + case ElementType.U4: + case ElementType.I4: + case ElementType.R4: + size = 4; + break; + case ElementType.U8: + case ElementType.I8: + case ElementType.R8: + size = 8; + break; + case ElementType.Ptr: + case ElementType.FnPtr: + size = IntPtr.Size; + break; + case ElementType.CModOpt: + case ElementType.CModReqD: + return GetFieldTypeSize (((IModifierType)type).ElementType); + default: + var field_type = type.Resolve (); + if (field_type != null && field_type.HasLayoutInfo) + size = field_type.ClassSize; + + break; + } + + return size; + } + + void InitializeFieldRVAs () + { + if (metadata.FieldRVAs != null) + return; + + int length = MoveTo (Table.FieldRVA); + + var field_rvas = metadata.FieldRVAs = new Dictionary (length); + + for (int i = 0; i < length; i++) { + var rva = ReadUInt32 (); + var field = ReadTableIndex (Table.Field); + + field_rvas.Add (field, rva); + } + } + + public int ReadFieldLayout (FieldDefinition field) + { + InitializeFieldLayouts (); + var rid = field.token.RID; + uint offset; + if (!metadata.FieldLayouts.TryGetValue (rid, out offset)) + return Mixin.NoDataMarker; + + metadata.FieldLayouts.Remove (rid); + + return (int)offset; + } + + void InitializeFieldLayouts () + { + if (metadata.FieldLayouts != null) + return; + + int length = MoveTo (Table.FieldLayout); + + var field_layouts = metadata.FieldLayouts = new Dictionary (length); + + for (int i = 0; i < length; i++) { + var offset = ReadUInt32 (); + var field = ReadTableIndex (Table.Field); + + field_layouts.Add (field, offset); + } + } + + public bool HasEvents (TypeDefinition type) + { + InitializeEvents (); + + Range range; + if (!metadata.TryGetEventsRange (type, out range)) + return false; + + return range.Length > 0; + } + + public Collection ReadEvents (TypeDefinition type) + { + InitializeEvents (); + Range range; + + if (!metadata.TryGetEventsRange (type, out range)) + return new MemberDefinitionCollection (type); + + var events = new MemberDefinitionCollection (type, (int)range.Length); + + metadata.RemoveEventsRange (type); + + if (range.Length == 0) + return events; + + this.context = type; + + if (!MoveTo (Table.EventPtr, range.Start)) { + if (!MoveTo (Table.Event, range.Start)) + return events; + + for (uint i = 0; i < range.Length; i++) + ReadEvent (range.Start + i, events); + } else + ReadPointers (Table.EventPtr, Table.Event, range, events, ReadEvent); + + return events; + } + + void ReadEvent (uint event_rid, Collection events) + { + var attributes = (EventAttributes)ReadUInt16 (); + var name = ReadString (); + var event_type = GetTypeDefOrRef (ReadMetadataToken (CodedIndex.TypeDefOrRef)); + + var @event = new EventDefinition (name, attributes, event_type); + @event.token = new MetadataToken (TokenType.Event, event_rid); + + if (IsDeleted (@event)) + return; + + events.Add (@event); + } + + void InitializeEvents () + { + if (metadata.Events != null) + return; + + int length = MoveTo (Table.EventMap); + + metadata.Events = new Dictionary (length); + + for (uint i = 1; i <= length; i++) { + var type_rid = ReadTableIndex (Table.TypeDef); + Range events_range = ReadListRange (i, Table.EventMap, Table.Event); + metadata.AddEventsRange (type_rid, events_range); + } + } + + public bool HasProperties (TypeDefinition type) + { + InitializeProperties (); + + Range range; + if (!metadata.TryGetPropertiesRange (type, out range)) + return false; + + return range.Length > 0; + } + + public Collection ReadProperties (TypeDefinition type) + { + InitializeProperties (); + + Range range; + + if (!metadata.TryGetPropertiesRange (type, out range)) + return new MemberDefinitionCollection (type); + + metadata.RemovePropertiesRange (type); + + var properties = new MemberDefinitionCollection (type, (int)range.Length); + + if (range.Length == 0) + return properties; + + this.context = type; + + if (!MoveTo (Table.PropertyPtr, range.Start)) { + if (!MoveTo (Table.Property, range.Start)) + return properties; + for (uint i = 0; i < range.Length; i++) + ReadProperty (range.Start + i, properties); + } else + ReadPointers (Table.PropertyPtr, Table.Property, range, properties, ReadProperty); + + return properties; + } + + void ReadProperty (uint property_rid, Collection properties) + { + var attributes = (PropertyAttributes)ReadUInt16 (); + var name = ReadString (); + var signature = ReadBlobIndex (); + + var reader = ReadSignature (signature); + const byte property_signature = 0x8; + + var calling_convention = reader.ReadByte (); + + if ((calling_convention & property_signature) == 0) + throw new NotSupportedException (); + + var has_this = (calling_convention & 0x20) != 0; + + reader.ReadCompressedUInt32 (); // count + + var property = new PropertyDefinition (name, attributes, reader.ReadTypeSignature ()); + property.HasThis = has_this; + property.token = new MetadataToken (TokenType.Property, property_rid); + + if (IsDeleted (property)) + return; + + properties.Add (property); + } + + void InitializeProperties () + { + if (metadata.Properties != null) + return; + + int length = MoveTo (Table.PropertyMap); + + metadata.Properties = new Dictionary (length); + + for (uint i = 1; i <= length; i++) { + var type_rid = ReadTableIndex (Table.TypeDef); + var properties_range = ReadListRange (i, Table.PropertyMap, Table.Property); + metadata.AddPropertiesRange (type_rid, properties_range); + } + } + + MethodSemanticsAttributes ReadMethodSemantics (MethodDefinition method) + { + InitializeMethodSemantics (); + Row row; + if (!metadata.Semantics.TryGetValue (method.token.RID, out row)) + return MethodSemanticsAttributes.None; + + var type = method.DeclaringType; + + switch (row.Col1) { + case MethodSemanticsAttributes.AddOn: + GetEvent (type, row.Col2).add_method = method; + break; + case MethodSemanticsAttributes.Fire: + GetEvent (type, row.Col2).invoke_method = method; + break; + case MethodSemanticsAttributes.RemoveOn: + GetEvent (type, row.Col2).remove_method = method; + break; + case MethodSemanticsAttributes.Getter: + GetProperty (type, row.Col2).get_method = method; + break; + case MethodSemanticsAttributes.Setter: + GetProperty (type, row.Col2).set_method = method; + break; + case MethodSemanticsAttributes.Other: + switch (row.Col2.TokenType) { + case TokenType.Event: { + var @event = GetEvent (type, row.Col2); + if (@event.other_methods == null) + @event.other_methods = new Collection (); + + @event.other_methods.Add (method); + break; + } + case TokenType.Property: { + var property = GetProperty (type, row.Col2); + if (property.other_methods == null) + property.other_methods = new Collection (); + + property.other_methods.Add (method); + + break; + } + default: + throw new NotSupportedException (); + } + break; + default: + throw new NotSupportedException (); + } + + metadata.Semantics.Remove (method.token.RID); + + return row.Col1; + } + + static EventDefinition GetEvent (TypeDefinition type, MetadataToken token) + { + if (token.TokenType != TokenType.Event) + throw new ArgumentException (); + + return GetMember (type.Events, token); + } + + static PropertyDefinition GetProperty (TypeDefinition type, MetadataToken token) + { + if (token.TokenType != TokenType.Property) + throw new ArgumentException (); + + return GetMember (type.Properties, token); + } + + static TMember GetMember (Collection members, MetadataToken token) where TMember : IMemberDefinition + { + for (int i = 0; i < members.Count; i++) { + var member = members [i]; + if (member.MetadataToken == token) + return member; + } + + throw new ArgumentException (); + } + + void InitializeMethodSemantics () + { + if (metadata.Semantics != null) + return; + + int length = MoveTo (Table.MethodSemantics); + + var semantics = metadata.Semantics = new Dictionary> (0); + + for (uint i = 0; i < length; i++) { + var attributes = (MethodSemanticsAttributes)ReadUInt16 (); + var method_rid = ReadTableIndex (Table.Method); + var association = ReadMetadataToken (CodedIndex.HasSemantics); + + semantics [method_rid] = new Row (attributes, association); + } + } + + public void ReadMethods (PropertyDefinition property) + { + ReadAllSemantics (property.DeclaringType); + } + + public void ReadMethods (EventDefinition @event) + { + ReadAllSemantics (@event.DeclaringType); + } + + public void ReadAllSemantics (MethodDefinition method) + { + ReadAllSemantics (method.DeclaringType); + } + + void ReadAllSemantics (TypeDefinition type) + { + var methods = type.Methods; + for (int i = 0; i < methods.Count; i++) { + var method = methods [i]; + if (method.sem_attrs_ready) + continue; + + method.sem_attrs = ReadMethodSemantics (method); + method.sem_attrs_ready = true; + } + } + + public Collection ReadMethods (TypeDefinition type) + { + var methods_range = type.methods_range; + if (methods_range.Length == 0) + return new MemberDefinitionCollection (type); + + var methods = new MemberDefinitionCollection (type, (int)methods_range.Length); + if (!MoveTo (Table.MethodPtr, methods_range.Start)) { + if (!MoveTo (Table.Method, methods_range.Start)) + return methods; + + for (uint i = 0; i < methods_range.Length; i++) + ReadMethod (methods_range.Start + i, methods); + } else + ReadPointers (Table.MethodPtr, Table.Method, methods_range, methods, ReadMethod); + + return methods; + } + + void ReadPointers (Table ptr, Table table, Range range, Collection members, Action> reader) + where TMember : IMemberDefinition + { + for (uint i = 0; i < range.Length; i++) { + MoveTo (ptr, range.Start + i); + + var rid = ReadTableIndex (table); + MoveTo (table, rid); + + reader (rid, members); + } + } + + static bool IsDeleted (IMemberDefinition member) + { + return member.IsSpecialName && member.Name == "_Deleted"; + } + + void InitializeMethods () + { + if (metadata.Methods != null) + return; + + metadata.Methods = new MethodDefinition [image.GetTableLength (Table.Method)]; + } + + void ReadMethod (uint method_rid, Collection methods) + { + var method = new MethodDefinition (); + method.rva = ReadUInt32 (); + method.ImplAttributes = (MethodImplAttributes)ReadUInt16 (); + method.Attributes = (MethodAttributes)ReadUInt16 (); + method.Name = ReadString (); + method.token = new MetadataToken (TokenType.Method, method_rid); + + if (IsDeleted (method)) + return; + + methods.Add (method); // attach method + + var signature = ReadBlobIndex (); + var param_range = ReadListRange (method_rid, Table.Method, Table.Param); + + this.context = method; + + ReadMethodSignature (signature, method); + metadata.AddMethodDefinition (method); + + if (param_range.Length != 0) { + var position = base.position; + ReadParameters (method, param_range); + base.position = position; + } + + if (module.IsWindowsMetadata ()) + WindowsRuntimeProjections.Project (method); + } + + void ReadParameters (MethodDefinition method, Range param_range) + { + if (!MoveTo (Table.ParamPtr, param_range.Start)) { + if (!MoveTo (Table.Param, param_range.Start)) + return; + + for (uint i = 0; i < param_range.Length; i++) + ReadParameter (param_range.Start + i, method); + } else + ReadParameterPointers (method, param_range); + } + + void ReadParameterPointers (MethodDefinition method, Range range) + { + for (uint i = 0; i < range.Length; i++) { + MoveTo (Table.ParamPtr, range.Start + i); + + var rid = ReadTableIndex (Table.Param); + + MoveTo (Table.Param, rid); + + ReadParameter (rid, method); + } + } + + void ReadParameter (uint param_rid, MethodDefinition method) + { + var attributes = (ParameterAttributes)ReadUInt16 (); + var sequence = ReadUInt16 (); + var name = ReadString (); + + var parameter = sequence == 0 + ? method.MethodReturnType.Parameter + : method.Parameters [sequence - 1]; + + parameter.token = new MetadataToken (TokenType.Param, param_rid); + parameter.Name = name; + parameter.Attributes = attributes; + } + + void ReadMethodSignature (uint signature, IMethodSignature method) + { + var reader = ReadSignature (signature); + reader.ReadMethodSignature (method); + } + + public PInvokeInfo ReadPInvokeInfo (MethodDefinition method) + { + InitializePInvokes (); + Row row; + + var rid = method.token.RID; + + if (!metadata.PInvokes.TryGetValue (rid, out row)) + return null; + + metadata.PInvokes.Remove (rid); + + return new PInvokeInfo ( + row.Col1, + image.StringHeap.Read (row.Col2), + module.ModuleReferences [(int)row.Col3 - 1]); + } + + void InitializePInvokes () + { + if (metadata.PInvokes != null) + return; + + int length = MoveTo (Table.ImplMap); + + var pinvokes = metadata.PInvokes = new Dictionary> (length); + + for (int i = 1; i <= length; i++) { + var attributes = (PInvokeAttributes)ReadUInt16 (); + var method = ReadMetadataToken (CodedIndex.MemberForwarded); + var name = ReadStringIndex (); + var scope = ReadTableIndex (Table.File); + + if (method.TokenType != TokenType.Method) + continue; + + pinvokes.Add (method.RID, new Row (attributes, name, scope)); + } + } + + public bool HasGenericParameters (IGenericParameterProvider provider) + { + InitializeGenericParameters (); + + Range [] ranges; + if (!metadata.TryGetGenericParameterRanges (provider, out ranges)) + return false; + + return RangesSize (ranges) > 0; + } + + public Collection ReadGenericParameters (IGenericParameterProvider provider) + { + InitializeGenericParameters (); + + Range [] ranges; + if (!metadata.TryGetGenericParameterRanges (provider, out ranges)) + return new GenericParameterCollection (provider); + + metadata.RemoveGenericParameterRange (provider); + + var generic_parameters = new GenericParameterCollection (provider, RangesSize (ranges)); + + for (int i = 0; i < ranges.Length; i++) + ReadGenericParametersRange (ranges [i], provider, generic_parameters); + + return generic_parameters; + } + + void ReadGenericParametersRange (Range range, IGenericParameterProvider provider, GenericParameterCollection generic_parameters) + { + if (!MoveTo (Table.GenericParam, range.Start)) + return; + + for (uint i = 0; i < range.Length; i++) { + ReadUInt16 (); // index + var flags = (GenericParameterAttributes)ReadUInt16 (); + ReadMetadataToken (CodedIndex.TypeOrMethodDef); + var name = ReadString (); + + var parameter = new GenericParameter (name, provider); + parameter.token = new MetadataToken (TokenType.GenericParam, range.Start + i); + parameter.Attributes = flags; + + generic_parameters.Add (parameter); + } + } + + void InitializeGenericParameters () + { + if (metadata.GenericParameters != null) + return; + + metadata.GenericParameters = InitializeRanges ( + Table.GenericParam, () => { + Advance (4); + var next = ReadMetadataToken (CodedIndex.TypeOrMethodDef); + ReadStringIndex (); + return next; + }); + } + + Dictionary InitializeRanges (Table table, Func get_next) + { + int length = MoveTo (table); + var ranges = new Dictionary (length); + + if (length == 0) + return ranges; + + MetadataToken owner = MetadataToken.Zero; + Range range = new Range (1, 0); + + for (uint i = 1; i <= length; i++) { + var next = get_next (); + + if (i == 1) { + owner = next; + range.Length++; + } else if (next != owner) { + AddRange (ranges, owner, range); + range = new Range (i, 1); + owner = next; + } else + range.Length++; + } + + AddRange (ranges, owner, range); + + return ranges; + } + + static void AddRange (Dictionary ranges, MetadataToken owner, Range range) + { + if (owner.RID == 0) + return; + + Range [] slots; + if (!ranges.TryGetValue (owner, out slots)) { + ranges.Add (owner, new [] { range }); + return; + } + + ranges [owner] = slots.Add (range); + } + + public bool HasGenericConstraints (GenericParameter generic_parameter) + { + InitializeGenericConstraints (); + + Collection> mapping; + if (!metadata.TryGetGenericConstraintMapping (generic_parameter, out mapping)) + return false; + + return mapping.Count > 0; + } + + public GenericParameterConstraintCollection ReadGenericConstraints (GenericParameter generic_parameter) + { + InitializeGenericConstraints (); + + Collection> mapping; + if (!metadata.TryGetGenericConstraintMapping (generic_parameter, out mapping)) + return new GenericParameterConstraintCollection (generic_parameter); + + var constraints = new GenericParameterConstraintCollection (generic_parameter, mapping.Count); + + this.context = (IGenericContext)generic_parameter.Owner; + + for (int i = 0; i < mapping.Count; i++) { + constraints.Add ( + new GenericParameterConstraint ( + GetTypeDefOrRef (mapping [i].Col2), + new MetadataToken (TokenType.GenericParamConstraint, mapping [i].Col1))); + } + + metadata.RemoveGenericConstraintMapping (generic_parameter); + + return constraints; + } + + void InitializeGenericConstraints () + { + if (metadata.GenericConstraints != null) + return; + + var length = MoveTo (Table.GenericParamConstraint); + + metadata.GenericConstraints = new Dictionary>> (length); + + for (uint i = 1; i <= length; i++) { + AddGenericConstraintMapping ( + ReadTableIndex (Table.GenericParam), + new Row (i, ReadMetadataToken (CodedIndex.TypeDefOrRef))); + } + } + + void AddGenericConstraintMapping (uint generic_parameter, Row constraint) + { + metadata.SetGenericConstraintMapping ( + generic_parameter, + AddMapping (metadata.GenericConstraints, generic_parameter, constraint)); + } + + public bool HasOverrides (MethodDefinition method) + { + InitializeOverrides (); + Collection mapping; + + if (!metadata.TryGetOverrideMapping (method, out mapping)) + return false; + + return mapping.Count > 0; + } + + public Collection ReadOverrides (MethodDefinition method) + { + InitializeOverrides (); + + Collection mapping; + if (!metadata.TryGetOverrideMapping (method, out mapping)) + return new Collection (); + + var overrides = new Collection (mapping.Count); + + this.context = method; + + for (int i = 0; i < mapping.Count; i++) + overrides.Add ((MethodReference)LookupToken (mapping [i])); + + metadata.RemoveOverrideMapping (method); + + return overrides; + } + + void InitializeOverrides () + { + if (metadata.Overrides != null) + return; + + var length = MoveTo (Table.MethodImpl); + + metadata.Overrides = new Dictionary> (length); + + for (int i = 1; i <= length; i++) { + ReadTableIndex (Table.TypeDef); + + var method = ReadMetadataToken (CodedIndex.MethodDefOrRef); + if (method.TokenType != TokenType.Method) + throw new NotSupportedException (); + + var @override = ReadMetadataToken (CodedIndex.MethodDefOrRef); + + AddOverrideMapping (method.RID, @override); + } + } + + void AddOverrideMapping (uint method_rid, MetadataToken @override) + { + metadata.SetOverrideMapping ( + method_rid, + AddMapping (metadata.Overrides, method_rid, @override)); + } + + public MethodBody ReadMethodBody (MethodDefinition method) + { + return code.ReadMethodBody (method); + } + + public int ReadCodeSize (MethodDefinition method) + { + return code.ReadCodeSize (method); + } + + public CallSite ReadCallSite (MetadataToken token) + { + if (!MoveTo (Table.StandAloneSig, token.RID)) + return null; + + var signature = ReadBlobIndex (); + + var call_site = new CallSite (); + + ReadMethodSignature (signature, call_site); + + call_site.MetadataToken = token; + + return call_site; + } + + public VariableDefinitionCollection ReadVariables (MetadataToken local_var_token, MethodDefinition method = null) + { + if (!MoveTo (Table.StandAloneSig, local_var_token.RID)) + return null; + + var reader = ReadSignature (ReadBlobIndex ()); + const byte local_sig = 0x7; + + if (reader.ReadByte () != local_sig) + throw new NotSupportedException (); + + var count = reader.ReadCompressedUInt32 (); + if (count == 0) + return null; + + var variables = new VariableDefinitionCollection (method, (int)count); + + for (int i = 0; i < count; i++) + variables.Add (new VariableDefinition (reader.ReadTypeSignature ())); + + return variables; + } + + public IMetadataTokenProvider LookupToken (MetadataToken token) + { + var rid = token.RID; + + if (rid == 0) + return null; + + if (metadata_reader != null) + return metadata_reader.LookupToken (token); + + IMetadataTokenProvider element; + var position = this.position; + var context = this.context; + + switch (token.TokenType) { + case TokenType.TypeDef: + element = GetTypeDefinition (rid); + break; + case TokenType.TypeRef: + element = GetTypeReference (rid); + break; + case TokenType.TypeSpec: + element = GetTypeSpecification (rid); + break; + case TokenType.Field: + element = GetFieldDefinition (rid); + break; + case TokenType.Method: + element = GetMethodDefinition (rid); + break; + case TokenType.MemberRef: + element = GetMemberReference (rid); + break; + case TokenType.MethodSpec: + element = GetMethodSpecification (rid); + break; + default: + return null; + } + + this.position = position; + this.context = context; + + return element; + } + + public FieldDefinition GetFieldDefinition (uint rid) + { + InitializeTypeDefinitions (); + + var field = metadata.GetFieldDefinition (rid); + if (field != null) + return field; + + return LookupField (rid); + } + + FieldDefinition LookupField (uint rid) + { + var type = metadata.GetFieldDeclaringType (rid); + if (type == null) + return null; + + Mixin.Read (type.Fields); + + return metadata.GetFieldDefinition (rid); + } + + public MethodDefinition GetMethodDefinition (uint rid) + { + InitializeTypeDefinitions (); + + var method = metadata.GetMethodDefinition (rid); + if (method != null) + return method; + + return LookupMethod (rid); + } + + MethodDefinition LookupMethod (uint rid) + { + var type = metadata.GetMethodDeclaringType (rid); + if (type == null) + return null; + + Mixin.Read (type.Methods); + + return metadata.GetMethodDefinition (rid); + } + + MethodSpecification GetMethodSpecification (uint rid) + { + if (!MoveTo (Table.MethodSpec, rid)) + return null; + + var element_method = (MethodReference)LookupToken ( + ReadMetadataToken (CodedIndex.MethodDefOrRef)); + var signature = ReadBlobIndex (); + + var method_spec = ReadMethodSpecSignature (signature, element_method); + method_spec.token = new MetadataToken (TokenType.MethodSpec, rid); + return method_spec; + } + + MethodSpecification ReadMethodSpecSignature (uint signature, MethodReference method) + { + var reader = ReadSignature (signature); + const byte methodspec_sig = 0x0a; + + var call_conv = reader.ReadByte (); + + if (call_conv != methodspec_sig) + throw new NotSupportedException (); + + var arity = reader.ReadCompressedUInt32 (); + + var instance = new GenericInstanceMethod (method, (int)arity); + + reader.ReadGenericInstanceSignature (method, instance, arity); + + return instance; + } + + MemberReference GetMemberReference (uint rid) + { + InitializeMemberReferences (); + + var member = metadata.GetMemberReference (rid); + if (member != null) + return member; + + member = ReadMemberReference (rid); + if (member != null && !member.ContainsGenericParameter) + metadata.AddMemberReference (member); + return member; + } + + MemberReference ReadMemberReference (uint rid) + { + if (!MoveTo (Table.MemberRef, rid)) + return null; + + var token = ReadMetadataToken (CodedIndex.MemberRefParent); + var name = ReadString (); + var signature = ReadBlobIndex (); + + MemberReference member; + + switch (token.TokenType) { + case TokenType.TypeDef: + case TokenType.TypeRef: + case TokenType.TypeSpec: + member = ReadTypeMemberReference (token, name, signature); + break; + case TokenType.Method: + member = ReadMethodMemberReference (token, name, signature); + break; + default: + throw new NotSupportedException (); + } + + member.token = new MetadataToken (TokenType.MemberRef, rid); + return member; + } + + MemberReference ReadTypeMemberReference (MetadataToken type, string name, uint signature) + { + var declaring_type = GetTypeDefOrRef (type); + + if (!declaring_type.IsArray) + this.context = declaring_type; + + var member = ReadMemberReferenceSignature (signature, declaring_type); + member.Name = name; + + return member; + } + + MemberReference ReadMemberReferenceSignature (uint signature, TypeReference declaring_type) + { + var reader = ReadSignature (signature); + const byte field_sig = 0x6; + + if (reader.buffer [reader.position] == field_sig) { + reader.position++; + var field = new FieldReference (); + field.DeclaringType = declaring_type; + field.FieldType = reader.ReadTypeSignature (); + return field; + } else { + var method = new MethodReference (); + method.DeclaringType = declaring_type; + reader.ReadMethodSignature (method); + return method; + } + } + + MemberReference ReadMethodMemberReference (MetadataToken token, string name, uint signature) + { + var method = GetMethodDefinition (token.RID); + + this.context = method; + + var member = ReadMemberReferenceSignature (signature, method.DeclaringType); + member.Name = name; + + return member; + } + + void InitializeMemberReferences () + { + if (metadata.MemberReferences != null) + return; + + metadata.MemberReferences = new MemberReference [image.GetTableLength (Table.MemberRef)]; + } + + public IEnumerable GetMemberReferences () + { + InitializeMemberReferences (); + + var length = image.GetTableLength (Table.MemberRef); + + var type_system = module.TypeSystem; + + var context = new MethodDefinition (string.Empty, MethodAttributes.Static, type_system.Void); + context.DeclaringType = new TypeDefinition (string.Empty, string.Empty, TypeAttributes.Public); + + var member_references = new MemberReference [length]; + + for (uint i = 1; i <= length; i++) { + this.context = context; + member_references [i - 1] = GetMemberReference (i); + } + + return member_references; + } + + void InitializeConstants () + { + if (metadata.Constants != null) + return; + + var length = MoveTo (Table.Constant); + + var constants = metadata.Constants = new Dictionary> (length); + + for (uint i = 1; i <= length; i++) { + var type = (ElementType)ReadUInt16 (); + var owner = ReadMetadataToken (CodedIndex.HasConstant); + var signature = ReadBlobIndex (); + + constants.Add (owner, new Row (type, signature)); + } + } + + public TypeReference ReadConstantSignature (MetadataToken token) + { + if (token.TokenType != TokenType.Signature) + throw new NotSupportedException (); + + if (token.RID == 0) + return null; + + if (!MoveTo (Table.StandAloneSig, token.RID)) + return null; + + return ReadFieldType (ReadBlobIndex ()); + } + + public object ReadConstant (IConstantProvider owner) + { + InitializeConstants (); + + Row row; + if (!metadata.Constants.TryGetValue (owner.MetadataToken, out row)) + return Mixin.NoValue; + + metadata.Constants.Remove (owner.MetadataToken); + + return ReadConstantValue (row.Col1, row.Col2); + } + + object ReadConstantValue (ElementType etype, uint signature) + { + switch (etype) { + case ElementType.Class: + case ElementType.Object: + return null; + case ElementType.String: + return ReadConstantString (signature); + default: + return ReadConstantPrimitive (etype, signature); + } + } + + string ReadConstantString (uint signature) + { + byte [] blob; + int index, count; + + GetBlobView (signature, out blob, out index, out count); + if (count == 0) + return string.Empty; + + if ((count & 1) == 1) + count--; + + return Encoding.Unicode.GetString (blob, index, count); + } + + object ReadConstantPrimitive (ElementType type, uint signature) + { + var reader = ReadSignature (signature); + return reader.ReadConstantSignature (type); + } + + internal void InitializeCustomAttributes () + { + if (metadata.CustomAttributes != null) + return; + + metadata.CustomAttributes = InitializeRanges ( + Table.CustomAttribute, () => { + var next = ReadMetadataToken (CodedIndex.HasCustomAttribute); + ReadMetadataToken (CodedIndex.CustomAttributeType); + ReadBlobIndex (); + return next; + }); + } + + public bool HasCustomAttributes (ICustomAttributeProvider owner) + { + InitializeCustomAttributes (); + + Range [] ranges; + if (!metadata.TryGetCustomAttributeRanges (owner, out ranges)) + return false; + + return RangesSize (ranges) > 0; + } + + public Collection ReadCustomAttributes (ICustomAttributeProvider owner) + { + InitializeCustomAttributes (); + + Range [] ranges; + if (!metadata.TryGetCustomAttributeRanges (owner, out ranges)) + return new Collection (); + + var custom_attributes = new Collection (RangesSize (ranges)); + + for (int i = 0; i < ranges.Length; i++) + ReadCustomAttributeRange (ranges [i], custom_attributes); + + metadata.RemoveCustomAttributeRange (owner); + + if (module.IsWindowsMetadata ()) + foreach (var custom_attribute in custom_attributes) + WindowsRuntimeProjections.Project (owner, custom_attribute); + + return custom_attributes; + } + + void ReadCustomAttributeRange (Range range, Collection custom_attributes) + { + if (!MoveTo (Table.CustomAttribute, range.Start)) + return; + + for (var i = 0; i < range.Length; i++) { + ReadMetadataToken (CodedIndex.HasCustomAttribute); + + var constructor = (MethodReference)LookupToken ( + ReadMetadataToken (CodedIndex.CustomAttributeType)); + + var signature = ReadBlobIndex (); + + custom_attributes.Add (new CustomAttribute (signature, constructor)); + } + } + + static int RangesSize (Range [] ranges) + { + uint size = 0; + for (int i = 0; i < ranges.Length; i++) + size += ranges [i].Length; + + return (int)size; + } + + public IEnumerable GetCustomAttributes () + { + InitializeTypeDefinitions (); + + var length = image.TableHeap [Table.CustomAttribute].Length; + var custom_attributes = new Collection ((int)length); + ReadCustomAttributeRange (new Range (1, length), custom_attributes); + + return custom_attributes; + } + + public byte [] ReadCustomAttributeBlob (uint signature) + { + return ReadBlob (signature); + } + + public void ReadCustomAttributeSignature (CustomAttribute attribute) + { + var reader = ReadSignature (attribute.signature); + + if (!reader.CanReadMore ()) + return; + + if (reader.ReadUInt16 () != 0x0001) + throw new InvalidOperationException (); + + var constructor = attribute.Constructor; + if (constructor.HasParameters) + reader.ReadCustomAttributeConstructorArguments (attribute, constructor.Parameters); + + if (!reader.CanReadMore ()) + return; + + var named = reader.ReadUInt16 (); + + if (named == 0) + return; + + reader.ReadCustomAttributeNamedArguments (named, ref attribute.fields, ref attribute.properties); + } + + void InitializeMarshalInfos () + { + if (metadata.FieldMarshals != null) + return; + + var length = MoveTo (Table.FieldMarshal); + + var marshals = metadata.FieldMarshals = new Dictionary (length); + + for (int i = 0; i < length; i++) { + var token = ReadMetadataToken (CodedIndex.HasFieldMarshal); + var signature = ReadBlobIndex (); + if (token.RID == 0) + continue; + + marshals.Add (token, signature); + } + } + + public bool HasMarshalInfo (IMarshalInfoProvider owner) + { + InitializeMarshalInfos (); + + return metadata.FieldMarshals.ContainsKey (owner.MetadataToken); + } + + public MarshalInfo ReadMarshalInfo (IMarshalInfoProvider owner) + { + InitializeMarshalInfos (); + + uint signature; + if (!metadata.FieldMarshals.TryGetValue (owner.MetadataToken, out signature)) + return null; + + var reader = ReadSignature (signature); + + metadata.FieldMarshals.Remove (owner.MetadataToken); + + return reader.ReadMarshalInfo (); + } + + void InitializeSecurityDeclarations () + { + if (metadata.SecurityDeclarations != null) + return; + + metadata.SecurityDeclarations = InitializeRanges ( + Table.DeclSecurity, () => { + ReadUInt16 (); + var next = ReadMetadataToken (CodedIndex.HasDeclSecurity); + ReadBlobIndex (); + return next; + }); + } + + public bool HasSecurityDeclarations (ISecurityDeclarationProvider owner) + { + InitializeSecurityDeclarations (); + + Range [] ranges; + if (!metadata.TryGetSecurityDeclarationRanges (owner, out ranges)) + return false; + + return RangesSize (ranges) > 0; + } + + public Collection ReadSecurityDeclarations (ISecurityDeclarationProvider owner) + { + InitializeSecurityDeclarations (); + + Range [] ranges; + if (!metadata.TryGetSecurityDeclarationRanges (owner, out ranges)) + return new Collection (); + + var security_declarations = new Collection (RangesSize (ranges)); + + for (int i = 0; i < ranges.Length; i++) + ReadSecurityDeclarationRange (ranges [i], security_declarations); + + metadata.RemoveSecurityDeclarationRange (owner); + + return security_declarations; + } + + void ReadSecurityDeclarationRange (Range range, Collection security_declarations) + { + if (!MoveTo (Table.DeclSecurity, range.Start)) + return; + + for (int i = 0; i < range.Length; i++) { + var action = (SecurityAction)ReadUInt16 (); + ReadMetadataToken (CodedIndex.HasDeclSecurity); + var signature = ReadBlobIndex (); + + security_declarations.Add (new SecurityDeclaration (action, signature, module)); + } + } + + public byte [] ReadSecurityDeclarationBlob (uint signature) + { + return ReadBlob (signature); + } + + public void ReadSecurityDeclarationSignature (SecurityDeclaration declaration) + { + var signature = declaration.signature; + var reader = ReadSignature (signature); + + if (reader.buffer [reader.position] != '.') { + ReadXmlSecurityDeclaration (signature, declaration); + return; + } + + reader.position++; + var count = reader.ReadCompressedUInt32 (); + var attributes = new Collection ((int)count); + + for (int i = 0; i < count; i++) + attributes.Add (reader.ReadSecurityAttribute ()); + + declaration.security_attributes = attributes; + } + + void ReadXmlSecurityDeclaration (uint signature, SecurityDeclaration declaration) + { + var attributes = new Collection (1); + + var attribute = new SecurityAttribute ( + module.TypeSystem.LookupType ("System.Security.Permissions", "PermissionSetAttribute")); + + attribute.properties = new Collection (1); + attribute.properties.Add ( + new CustomAttributeNamedArgument ( + "XML", + new CustomAttributeArgument ( + module.TypeSystem.String, + ReadUnicodeStringBlob (signature)))); + + attributes.Add (attribute); + + declaration.security_attributes = attributes; + } + + public Collection ReadExportedTypes () + { + var length = MoveTo (Table.ExportedType); + if (length == 0) + return new Collection (); + + var exported_types = new Collection (length); + + for (int i = 1; i <= length; i++) { + var attributes = (TypeAttributes)ReadUInt32 (); + var identifier = ReadUInt32 (); + var name = ReadString (); + var @namespace = ReadString (); + var implementation = ReadMetadataToken (CodedIndex.Implementation); + + ExportedType declaring_type = null; + IMetadataScope scope = null; + + switch (implementation.TokenType) { + case TokenType.AssemblyRef: + case TokenType.File: + scope = GetExportedTypeScope (implementation); + break; + case TokenType.ExportedType: + // FIXME: if the table is not properly sorted + declaring_type = exported_types [(int)implementation.RID - 1]; + break; + } + + var exported_type = new ExportedType (@namespace, name, module, scope) { + Attributes = attributes, + Identifier = (int)identifier, + DeclaringType = declaring_type, + }; + exported_type.token = new MetadataToken (TokenType.ExportedType, i); + + exported_types.Add (exported_type); + } + + return exported_types; + } + + IMetadataScope GetExportedTypeScope (MetadataToken token) + { + var position = this.position; + IMetadataScope scope; + + switch (token.TokenType) { + case TokenType.AssemblyRef: + InitializeAssemblyReferences (); + scope = metadata.GetAssemblyNameReference (token.RID); + break; + case TokenType.File: + InitializeModuleReferences (); + scope = GetModuleReferenceFromFile (token); + break; + default: + throw new NotSupportedException (); + } + + this.position = position; + return scope; + } + + ModuleReference GetModuleReferenceFromFile (MetadataToken token) + { + if (!MoveTo (Table.File, token.RID)) + return null; + + ReadUInt32 (); + var file_name = ReadString (); + var modules = module.ModuleReferences; + + ModuleReference reference; + for (int i = 0; i < modules.Count; i++) { + reference = modules [i]; + if (reference.Name == file_name) + return reference; + } + + reference = new ModuleReference (file_name); + modules.Add (reference); + return reference; + } + + void InitializeDocuments () + { + if (metadata.Documents != null) + return; + + int length = MoveTo (Table.Document); + + var documents = metadata.Documents = new Document [length]; + + for (uint i = 1; i <= length; i++) { + var name_index = ReadBlobIndex (); + var hash_algorithm = ReadGuid (); + var hash = ReadBlob (); + var language = ReadGuid (); + + var signature = ReadSignature (name_index); + var name = signature.ReadDocumentName (); + + documents [i - 1] = new Document (name) { + HashAlgorithmGuid = hash_algorithm, + Hash = hash, + LanguageGuid = language, + token = new MetadataToken (TokenType.Document, i), + }; + } + } + + public Collection ReadSequencePoints (MethodDefinition method) + { + InitializeDocuments (); + + if (!MoveTo (Table.MethodDebugInformation, method.MetadataToken.RID)) + return new Collection (0); + + var document_index = ReadTableIndex (Table.Document); + var signature = ReadBlobIndex (); + if (signature == 0) + return new Collection (0); + + var document = GetDocument (document_index); + var reader = ReadSignature (signature); + + return reader.ReadSequencePoints (document); + } + + public Document GetDocument (uint rid) + { + var document = metadata.GetDocument (rid); + if (document == null) + return null; + + document.custom_infos = GetCustomDebugInformation (document); + return document; + } + + void InitializeLocalScopes () + { + if (metadata.LocalScopes != null) + return; + + InitializeMethods (); + + int length = MoveTo (Table.LocalScope); + + metadata.LocalScopes = new Dictionary>> (); + + for (uint i = 1; i <= length; i++) { + var method = ReadTableIndex (Table.Method); + var import = ReadTableIndex (Table.ImportScope); + var variables = ReadListRange (i, Table.LocalScope, Table.LocalVariable); + var constants = ReadListRange (i, Table.LocalScope, Table.LocalConstant); + var scope_start = ReadUInt32 (); + var scope_length = ReadUInt32 (); + + metadata.SetLocalScopes (method, AddMapping (metadata.LocalScopes, method, new Row (import, variables, constants, scope_start, scope_length, i))); + } + } + + public ScopeDebugInformation ReadScope (MethodDefinition method) + { + InitializeLocalScopes (); + InitializeImportScopes (); + + Collection> records; + if (!metadata.TryGetLocalScopes (method, out records)) + return null; + + var method_scope = null as ScopeDebugInformation; + + for (int i = 0; i < records.Count; i++) { + var scope = ReadLocalScope (records [i]); + + if (i == 0) { + method_scope = scope; + continue; + } + + if (!AddScope (method_scope.scopes, scope)) + method_scope.Scopes.Add (scope); + } + + return method_scope; + } + + static bool AddScope (Collection scopes, ScopeDebugInformation scope) + { + if (scopes.IsNullOrEmpty ()) + return false; + + foreach (var sub_scope in scopes) { + if (sub_scope.HasScopes && AddScope (sub_scope.Scopes, scope)) + return true; + + if (scope.Start.Offset >= sub_scope.Start.Offset && scope.End.Offset <= sub_scope.End.Offset) { + sub_scope.Scopes.Add (scope); + return true; + } + } + + return false; + } + + ScopeDebugInformation ReadLocalScope (Row record) + { + var scope = new ScopeDebugInformation { + start = new InstructionOffset ((int)record.Col4), + end = new InstructionOffset ((int)(record.Col4 + record.Col5)), + token = new MetadataToken (TokenType.LocalScope, record.Col6), + }; + + if (record.Col1 > 0) + scope.import = metadata.GetImportScope (record.Col1); + + if (record.Col2.Length > 0) { + scope.variables = new Collection ((int)record.Col2.Length); + for (uint i = 0; i < record.Col2.Length; i++) { + var variable = ReadLocalVariable (record.Col2.Start + i); + if (variable != null) + scope.variables.Add (variable); + } + } + + if (record.Col3.Length > 0) { + scope.constants = new Collection ((int)record.Col3.Length); + for (uint i = 0; i < record.Col3.Length; i++) { + var constant = ReadLocalConstant (record.Col3.Start + i); + if (constant != null) + scope.constants.Add (constant); + } + } + + return scope; + } + + VariableDebugInformation ReadLocalVariable (uint rid) + { + if (!MoveTo (Table.LocalVariable, rid)) + return null; + + var attributes = (VariableAttributes)ReadUInt16 (); + var index = ReadUInt16 (); + var name = ReadString (); + + var variable = new VariableDebugInformation (index, name) { Attributes = attributes, token = new MetadataToken (TokenType.LocalVariable, rid) }; + variable.custom_infos = GetCustomDebugInformation (variable); + return variable; + } + + ConstantDebugInformation ReadLocalConstant (uint rid) + { + if (!MoveTo (Table.LocalConstant, rid)) + return null; + + var name = ReadString (); + var signature = ReadSignature (ReadBlobIndex ()); + var type = signature.ReadTypeSignature (); + + object value; + if (type.etype == ElementType.String) { + if (signature.CanReadMore () && signature.buffer [signature.position] != 0xff) { + var bytes = signature.ReadBytes ((int)(signature.sig_length - (signature.position - signature.start))); + value = Encoding.Unicode.GetString (bytes, 0, bytes.Length); + } else + value = null; + } else if (type.IsTypeOf ("System", "Decimal")) { + var b = signature.ReadByte (); + value = new decimal (signature.ReadInt32 (), signature.ReadInt32 (), signature.ReadInt32 (), (b & 0x80) != 0, (byte)(b & 0x7f)); + } else if (type.IsTypeOf ("System", "DateTime")) { + value = new DateTime (signature.ReadInt64 ()); + } else if (type.etype == ElementType.Object || type.etype == ElementType.None || type.etype == ElementType.Class || type.etype == ElementType.Array || type.etype == ElementType.GenericInst) { + value = null; + } else + value = signature.ReadConstantSignature (type.etype); + + var constant = new ConstantDebugInformation (name, type, value) { token = new MetadataToken (TokenType.LocalConstant, rid) }; + constant.custom_infos = GetCustomDebugInformation (constant); + return constant; + } + + void InitializeImportScopes () + { + if (metadata.ImportScopes != null) + return; + + var length = MoveTo (Table.ImportScope); + + metadata.ImportScopes = new ImportDebugInformation [length]; + + for (int i = 1; i <= length; i++) { + ReadTableIndex (Table.ImportScope); + + var import = new ImportDebugInformation (); + import.token = new MetadataToken (TokenType.ImportScope, i); + + var signature = ReadSignature (ReadBlobIndex ()); + while (signature.CanReadMore ()) + import.Targets.Add (ReadImportTarget (signature)); + + metadata.ImportScopes [i - 1] = import; + } + + MoveTo (Table.ImportScope); + + for (int i = 0; i < length; i++) { + var parent = ReadTableIndex (Table.ImportScope); + + ReadBlobIndex (); + + if (parent != 0) + metadata.ImportScopes [i].Parent = metadata.GetImportScope (parent); + } + } + + public string ReadUTF8StringBlob (uint signature) + { + return ReadStringBlob (signature, Encoding.UTF8); + } + + string ReadUnicodeStringBlob (uint signature) + { + return ReadStringBlob (signature, Encoding.Unicode); + } + + string ReadStringBlob (uint signature, Encoding encoding) + { + byte [] blob; + int index, count; + + GetBlobView (signature, out blob, out index, out count); + if (count == 0) + return string.Empty; + + return encoding.GetString (blob, index, count); + } + + ImportTarget ReadImportTarget (SignatureReader signature) + { + AssemblyNameReference reference = null; + string @namespace = null; + string alias = null; + TypeReference type = null; + + var kind = (ImportTargetKind)signature.ReadCompressedUInt32 (); + switch (kind) { + case ImportTargetKind.ImportNamespace: + @namespace = ReadUTF8StringBlob (signature.ReadCompressedUInt32 ()); + break; + case ImportTargetKind.ImportNamespaceInAssembly: + reference = metadata.GetAssemblyNameReference (signature.ReadCompressedUInt32 ()); + @namespace = ReadUTF8StringBlob (signature.ReadCompressedUInt32 ()); + break; + case ImportTargetKind.ImportType: + type = signature.ReadTypeToken (); + break; + case ImportTargetKind.ImportXmlNamespaceWithAlias: + alias = ReadUTF8StringBlob (signature.ReadCompressedUInt32 ()); + @namespace = ReadUTF8StringBlob (signature.ReadCompressedUInt32 ()); + break; + case ImportTargetKind.ImportAlias: + alias = ReadUTF8StringBlob (signature.ReadCompressedUInt32 ()); + break; + case ImportTargetKind.DefineAssemblyAlias: + alias = ReadUTF8StringBlob (signature.ReadCompressedUInt32 ()); + reference = metadata.GetAssemblyNameReference (signature.ReadCompressedUInt32 ()); + break; + case ImportTargetKind.DefineNamespaceAlias: + alias = ReadUTF8StringBlob (signature.ReadCompressedUInt32 ()); + @namespace = ReadUTF8StringBlob (signature.ReadCompressedUInt32 ()); + break; + case ImportTargetKind.DefineNamespaceInAssemblyAlias: + alias = ReadUTF8StringBlob (signature.ReadCompressedUInt32 ()); + reference = metadata.GetAssemblyNameReference (signature.ReadCompressedUInt32 ()); + @namespace = ReadUTF8StringBlob (signature.ReadCompressedUInt32 ()); + break; + case ImportTargetKind.DefineTypeAlias: + alias = ReadUTF8StringBlob (signature.ReadCompressedUInt32 ()); + type = signature.ReadTypeToken (); + break; + } + + return new ImportTarget (kind) { + alias = alias, + type = type, + @namespace = @namespace, + reference = reference, + }; + } + + void InitializeStateMachineMethods () + { + if (metadata.StateMachineMethods != null) + return; + + var length = MoveTo (Table.StateMachineMethod); + + metadata.StateMachineMethods = new Dictionary (length); + + for (int i = 0; i < length; i++) + metadata.StateMachineMethods.Add (ReadTableIndex (Table.Method), ReadTableIndex (Table.Method)); + } + + public MethodDefinition ReadStateMachineKickoffMethod (MethodDefinition method) + { + InitializeStateMachineMethods (); + + uint rid; + if (!metadata.TryGetStateMachineKickOffMethod (method, out rid)) + return null; + + return GetMethodDefinition (rid); + } + + void InitializeCustomDebugInformations () + { + if (metadata.CustomDebugInformations != null) + return; + + var length = MoveTo (Table.CustomDebugInformation); + + metadata.CustomDebugInformations = new Dictionary []> (); + + for (uint i = 1; i <= length; i++) { + var token = ReadMetadataToken (CodedIndex.HasCustomDebugInformation); + var info = new Row (ReadGuid (), ReadBlobIndex (), i); + + Row [] infos; + metadata.CustomDebugInformations.TryGetValue (token, out infos); + metadata.CustomDebugInformations [token] = infos.Add (info); + } + } + + public Collection GetCustomDebugInformation (ICustomDebugInformationProvider provider) + { + InitializeCustomDebugInformations (); + + Row [] rows; + if (!metadata.CustomDebugInformations.TryGetValue (provider.MetadataToken, out rows)) + return null; + + var infos = new Collection (rows.Length); + + for (int i = 0; i < rows.Length; i++) { + if (rows [i].Col1 == StateMachineScopeDebugInformation.KindIdentifier) { + var signature = ReadSignature (rows [i].Col2); + var scopes = new Collection (); + + while (signature.CanReadMore ()) { + var start = signature.ReadInt32 (); + var end = start + signature.ReadInt32 (); + scopes.Add (new StateMachineScope (start, end)); + } + + var state_machine = new StateMachineScopeDebugInformation (); + state_machine.scopes = scopes; + + infos.Add (state_machine); + } else if (rows [i].Col1 == AsyncMethodBodyDebugInformation.KindIdentifier) { + var signature = ReadSignature (rows [i].Col2); + + var catch_offset = signature.ReadInt32 () - 1; + var yields = new Collection (); + var resumes = new Collection (); + var resume_methods = new Collection (); + + while (signature.CanReadMore ()) { + yields.Add (new InstructionOffset (signature.ReadInt32 ())); + resumes.Add (new InstructionOffset (signature.ReadInt32 ())); + resume_methods.Add (GetMethodDefinition (signature.ReadCompressedUInt32 ())); + } + + var async_body = new AsyncMethodBodyDebugInformation (catch_offset); + async_body.yields = yields; + async_body.resumes = resumes; + async_body.resume_methods = resume_methods; + + infos.Add (async_body); + } else if (rows [i].Col1 == EmbeddedSourceDebugInformation.KindIdentifier) { + infos.Add (new EmbeddedSourceDebugInformation (rows [i].Col2, this)); + } else if (rows [i].Col1 == SourceLinkDebugInformation.KindIdentifier) { + infos.Add (new SourceLinkDebugInformation (Encoding.UTF8.GetString (ReadBlob (rows [i].Col2)))); + } else { + infos.Add (new BinaryCustomDebugInformation (rows [i].Col1, ReadBlob (rows [i].Col2))); + } + + infos [i].token = new MetadataToken (TokenType.CustomDebugInformation, rows [i].Col3); + } + + return infos; + } + + public byte [] ReadRawEmbeddedSourceDebugInformation (uint index) + { + var signature = ReadSignature (index); + return signature.ReadBytes ((int)signature.sig_length); + } + + public Row ReadEmbeddedSourceDebugInformation (uint index) + { + var signature = ReadSignature (index); + var format = signature.ReadInt32 (); + var length = signature.sig_length - 4; + + if (format == 0) { + return new Row (signature.ReadBytes ((int)length), false); + } else if (format > 0) { + var compressed_stream = new MemoryStream (signature.ReadBytes ((int)length)); + var decompressed_document = new byte [format]; // if positive, format is the decompressed length of the document + var decompressed_stream = new MemoryStream (decompressed_document); + + using (var deflate_stream = new DeflateStream (compressed_stream, CompressionMode.Decompress, leaveOpen: true)) + deflate_stream.CopyTo (decompressed_stream); + + return new Row (decompressed_document, true); + } else + throw new NotSupportedException (); + } + } + + sealed class SignatureReader : ByteBuffer { + + readonly MetadataReader reader; + readonly internal uint start, sig_length; + + TypeSystem TypeSystem { + get { return reader.module.TypeSystem; } + } + + public SignatureReader (uint blob, MetadataReader reader) + : base (reader.image.BlobHeap.data) + { + this.reader = reader; + this.position = (int)blob; + this.sig_length = ReadCompressedUInt32 (); + this.start = (uint)this.position; + } + + MetadataToken ReadTypeTokenSignature () + { + return CodedIndex.TypeDefOrRef.GetMetadataToken (ReadCompressedUInt32 ()); + } + + GenericParameter GetGenericParameter (GenericParameterType type, uint var) + { + var context = reader.context; + int index = (int)var; + + if (context == null) + return GetUnboundGenericParameter (type, index); + + IGenericParameterProvider provider; + + switch (type) { + case GenericParameterType.Type: + provider = context.Type; + break; + case GenericParameterType.Method: + provider = context.Method; + break; + default: + throw new NotSupportedException (); + } + + if (!context.IsDefinition) + CheckGenericContext (provider, index); + + if (index >= provider.GenericParameters.Count) + return GetUnboundGenericParameter (type, index); + + return provider.GenericParameters [index]; + } + + GenericParameter GetUnboundGenericParameter (GenericParameterType type, int index) + { + return new GenericParameter (index, type, reader.module); + } + + static void CheckGenericContext (IGenericParameterProvider owner, int index) + { + var owner_parameters = owner.GenericParameters; + + for (int i = owner_parameters.Count; i <= index; i++) + owner_parameters.Add (new GenericParameter (owner)); + } + + public void ReadGenericInstanceSignature (IGenericParameterProvider provider, IGenericInstance instance, uint arity) + { + if (!provider.IsDefinition) + CheckGenericContext (provider, (int)arity - 1); + + var instance_arguments = instance.GenericArguments; + + for (int i = 0; i < arity; i++) + instance_arguments.Add (ReadTypeSignature ()); + } + + ArrayType ReadArrayTypeSignature () + { + var array = new ArrayType (ReadTypeSignature ()); + + var rank = ReadCompressedUInt32 (); + + var sizes = new uint [ReadCompressedUInt32 ()]; + for (int i = 0; i < sizes.Length; i++) + sizes [i] = ReadCompressedUInt32 (); + + var low_bounds = new int [ReadCompressedUInt32 ()]; + for (int i = 0; i < low_bounds.Length; i++) + low_bounds [i] = ReadCompressedInt32 (); + + array.Dimensions.Clear (); + + for (int i = 0; i < rank; i++) { + int? lower = null, upper = null; + + if (i < low_bounds.Length) + lower = low_bounds [i]; + + if (i < sizes.Length) + upper = lower + (int)sizes [i] - 1; + + array.Dimensions.Add (new ArrayDimension (lower, upper)); + } + + return array; + } + + TypeReference GetTypeDefOrRef (MetadataToken token) + { + return reader.GetTypeDefOrRef (token); + } + + public TypeReference ReadTypeSignature () + { + return ReadTypeSignature ((ElementType)ReadByte ()); + } + + public TypeReference ReadTypeToken () + { + return GetTypeDefOrRef (ReadTypeTokenSignature ()); + } + + TypeReference ReadTypeSignature (ElementType etype) + { + switch (etype) { + case ElementType.ValueType: { + var value_type = GetTypeDefOrRef (ReadTypeTokenSignature ()); + value_type.KnownValueType (); + return value_type; + } + case ElementType.Class: + return GetTypeDefOrRef (ReadTypeTokenSignature ()); + case ElementType.Ptr: + return new PointerType (ReadTypeSignature ()); + case ElementType.FnPtr: { + var fptr = new FunctionPointerType (); + ReadMethodSignature (fptr); + return fptr; + } + case ElementType.ByRef: + return new ByReferenceType (ReadTypeSignature ()); + case ElementType.Pinned: + return new PinnedType (ReadTypeSignature ()); + case ElementType.SzArray: + return new ArrayType (ReadTypeSignature ()); + case ElementType.Array: + return ReadArrayTypeSignature (); + case ElementType.CModOpt: + return new OptionalModifierType ( + GetTypeDefOrRef (ReadTypeTokenSignature ()), ReadTypeSignature ()); + case ElementType.CModReqD: + return new RequiredModifierType ( + GetTypeDefOrRef (ReadTypeTokenSignature ()), ReadTypeSignature ()); + case ElementType.Sentinel: + return new SentinelType (ReadTypeSignature ()); + case ElementType.Var: + return GetGenericParameter (GenericParameterType.Type, ReadCompressedUInt32 ()); + case ElementType.MVar: + return GetGenericParameter (GenericParameterType.Method, ReadCompressedUInt32 ()); + case ElementType.GenericInst: { + var is_value_type = ReadByte () == (byte)ElementType.ValueType; + var element_type = GetTypeDefOrRef (ReadTypeTokenSignature ()); + + var arity = ReadCompressedUInt32 (); + var generic_instance = new GenericInstanceType (element_type, (int)arity); + + ReadGenericInstanceSignature (element_type, generic_instance, arity); + + if (is_value_type) { + generic_instance.KnownValueType (); + element_type.GetElementType ().KnownValueType (); + } + + return generic_instance; + } + case ElementType.Object: return TypeSystem.Object; + case ElementType.Void: return TypeSystem.Void; + case ElementType.TypedByRef: return TypeSystem.TypedReference; + case ElementType.I: return TypeSystem.IntPtr; + case ElementType.U: return TypeSystem.UIntPtr; + default: return GetPrimitiveType (etype); + } + } + + public void ReadMethodSignature (IMethodSignature method) + { + var calling_convention = ReadByte (); + + const byte has_this = 0x20; + const byte explicit_this = 0x40; + + if ((calling_convention & has_this) != 0) { + method.HasThis = true; + calling_convention = (byte)(calling_convention & ~has_this); + } + + if ((calling_convention & explicit_this) != 0) { + method.ExplicitThis = true; + calling_convention = (byte)(calling_convention & ~explicit_this); + } + + method.CallingConvention = (MethodCallingConvention)calling_convention; + + var generic_context = method as MethodReference; + if (generic_context != null && !generic_context.DeclaringType.IsArray) + reader.context = generic_context; + + if ((calling_convention & 0x10) != 0) { + var arity = ReadCompressedUInt32 (); + + if (generic_context != null && !generic_context.IsDefinition) + CheckGenericContext (generic_context, (int)arity - 1); + } + + var param_count = ReadCompressedUInt32 (); + + method.MethodReturnType.ReturnType = ReadTypeSignature (); + + if (param_count == 0) + return; + + Collection parameters; + + var method_ref = method as MethodReference; + if (method_ref != null) + parameters = method_ref.parameters = new ParameterDefinitionCollection (method, (int)param_count); + else + parameters = method.Parameters; + + for (int i = 0; i < param_count; i++) + parameters.Add (new ParameterDefinition (ReadTypeSignature ())); + } + + public object ReadConstantSignature (ElementType type) + { + return ReadPrimitiveValue (type); + } + + public void ReadCustomAttributeConstructorArguments (CustomAttribute attribute, Collection parameters) + { + var count = parameters.Count; + if (count == 0) + return; + + attribute.arguments = new Collection (count); + + for (int i = 0; i < count; i++) + attribute.arguments.Add ( + ReadCustomAttributeFixedArgument (parameters [i].ParameterType)); + } + + CustomAttributeArgument ReadCustomAttributeFixedArgument (TypeReference type) + { + if (type.IsArray) + return ReadCustomAttributeFixedArrayArgument ((ArrayType)type); + + return ReadCustomAttributeElement (type); + } + + public void ReadCustomAttributeNamedArguments (ushort count, ref Collection fields, ref Collection properties) + { + for (int i = 0; i < count; i++) { + if (!CanReadMore ()) + return; + ReadCustomAttributeNamedArgument (ref fields, ref properties); + } + } + + void ReadCustomAttributeNamedArgument (ref Collection fields, ref Collection properties) + { + var kind = ReadByte (); + var type = ReadCustomAttributeFieldOrPropType (); + var name = ReadUTF8String (); + + Collection container; + switch (kind) { + case 0x53: + container = GetCustomAttributeNamedArgumentCollection (ref fields); + break; + case 0x54: + container = GetCustomAttributeNamedArgumentCollection (ref properties); + break; + default: + throw new NotSupportedException (); + } + + container.Add (new CustomAttributeNamedArgument (name, ReadCustomAttributeFixedArgument (type))); + } + + static Collection GetCustomAttributeNamedArgumentCollection (ref Collection collection) + { + if (collection != null) + return collection; + + return collection = new Collection (); + } + + CustomAttributeArgument ReadCustomAttributeFixedArrayArgument (ArrayType type) + { + var length = ReadUInt32 (); + + if (length == 0xffffffff) + return new CustomAttributeArgument (type, null); + + if (length == 0) + return new CustomAttributeArgument (type, Empty.Array); + + var arguments = new CustomAttributeArgument [length]; + var element_type = type.ElementType; + + for (int i = 0; i < length; i++) + arguments [i] = ReadCustomAttributeElement (element_type); + + return new CustomAttributeArgument (type, arguments); + } + + CustomAttributeArgument ReadCustomAttributeElement (TypeReference type) + { + if (type.IsArray) + return ReadCustomAttributeFixedArrayArgument ((ArrayType)type); + + return new CustomAttributeArgument ( + type, + type.etype == ElementType.Object + ? ReadCustomAttributeElement (ReadCustomAttributeFieldOrPropType ()) + : ReadCustomAttributeElementValue (type)); + } + + object ReadCustomAttributeElementValue (TypeReference type) + { + var etype = type.etype; + + switch (etype) { + case ElementType.String: + return ReadUTF8String (); + case ElementType.None: + if (type.IsTypeOf ("System", "Type")) + return ReadTypeReference (); + + return ReadCustomAttributeEnum (type); + default: + return ReadPrimitiveValue (etype); + } + } + + object ReadPrimitiveValue (ElementType type) + { + switch (type) { + case ElementType.Boolean: + return ReadByte () == 1; + case ElementType.I1: + return (sbyte)ReadByte (); + case ElementType.U1: + return ReadByte (); + case ElementType.Char: + return (char)ReadUInt16 (); + case ElementType.I2: + return ReadInt16 (); + case ElementType.U2: + return ReadUInt16 (); + case ElementType.I4: + return ReadInt32 (); + case ElementType.U4: + return ReadUInt32 (); + case ElementType.I8: + return ReadInt64 (); + case ElementType.U8: + return ReadUInt64 (); + case ElementType.R4: + return ReadSingle (); + case ElementType.R8: + return ReadDouble (); + default: + throw new NotImplementedException (type.ToString ()); + } + } + + TypeReference GetPrimitiveType (ElementType etype) + { + switch (etype) { + case ElementType.Boolean: + return TypeSystem.Boolean; + case ElementType.Char: + return TypeSystem.Char; + case ElementType.I1: + return TypeSystem.SByte; + case ElementType.U1: + return TypeSystem.Byte; + case ElementType.I2: + return TypeSystem.Int16; + case ElementType.U2: + return TypeSystem.UInt16; + case ElementType.I4: + return TypeSystem.Int32; + case ElementType.U4: + return TypeSystem.UInt32; + case ElementType.I8: + return TypeSystem.Int64; + case ElementType.U8: + return TypeSystem.UInt64; + case ElementType.R4: + return TypeSystem.Single; + case ElementType.R8: + return TypeSystem.Double; + case ElementType.String: + return TypeSystem.String; + default: + throw new NotImplementedException (etype.ToString ()); + } + } + + TypeReference ReadCustomAttributeFieldOrPropType () + { + var etype = (ElementType)ReadByte (); + + switch (etype) { + case ElementType.Boxed: + return TypeSystem.Object; + case ElementType.SzArray: + return new ArrayType (ReadCustomAttributeFieldOrPropType ()); + case ElementType.Enum: + return ReadTypeReference (); + case ElementType.Type: + return TypeSystem.LookupType ("System", "Type"); + default: + return GetPrimitiveType (etype); + } + } + + public TypeReference ReadTypeReference () + { + return TypeParser.ParseType (reader.module, ReadUTF8String ()); + } + + object ReadCustomAttributeEnum (TypeReference enum_type) + { + var type = enum_type.CheckedResolve (); + if (!type.IsEnum) + throw new ArgumentException (); + + return ReadCustomAttributeElementValue (type.GetEnumUnderlyingType ()); + } + + public SecurityAttribute ReadSecurityAttribute () + { + var attribute = new SecurityAttribute (ReadTypeReference ()); + + ReadCompressedUInt32 (); + + ReadCustomAttributeNamedArguments ( + (ushort)ReadCompressedUInt32 (), + ref attribute.fields, + ref attribute.properties); + + return attribute; + } + + public MarshalInfo ReadMarshalInfo () + { + var native = ReadNativeType (); + switch (native) { + case NativeType.Array: { + var array = new ArrayMarshalInfo (); + if (CanReadMore ()) + array.element_type = ReadNativeType (); + if (CanReadMore ()) + array.size_parameter_index = (int)ReadCompressedUInt32 (); + if (CanReadMore ()) + array.size = (int)ReadCompressedUInt32 (); + if (CanReadMore ()) + array.size_parameter_multiplier = (int)ReadCompressedUInt32 (); + return array; + } + case NativeType.SafeArray: { + var array = new SafeArrayMarshalInfo (); + if (CanReadMore ()) + array.element_type = ReadVariantType (); + return array; + } + case NativeType.FixedArray: { + var array = new FixedArrayMarshalInfo (); + if (CanReadMore ()) + array.size = (int)ReadCompressedUInt32 (); + if (CanReadMore ()) + array.element_type = ReadNativeType (); + return array; + } + case NativeType.FixedSysString: { + var sys_string = new FixedSysStringMarshalInfo (); + if (CanReadMore ()) + sys_string.size = (int)ReadCompressedUInt32 (); + return sys_string; + } + case NativeType.CustomMarshaler: { + var marshaler = new CustomMarshalInfo (); + var guid_value = ReadUTF8String (); + marshaler.guid = !string.IsNullOrEmpty (guid_value) ? new Guid (guid_value) : Guid.Empty; + marshaler.unmanaged_type = ReadUTF8String (); + marshaler.managed_type = ReadTypeReference (); + marshaler.cookie = ReadUTF8String (); + return marshaler; + } + default: + return new MarshalInfo (native); + } + } + + NativeType ReadNativeType () + { + return (NativeType)ReadByte (); + } + + VariantType ReadVariantType () + { + return (VariantType)ReadByte (); + } + + string ReadUTF8String () + { + if (buffer [position] == 0xff) { + position++; + return null; + } + + var length = (int)ReadCompressedUInt32 (); + if (length == 0) + return string.Empty; + + if (position + length > buffer.Length) + return string.Empty; + + var @string = Encoding.UTF8.GetString (buffer, position, length); + + position += length; + return @string; + } + + public string ReadDocumentName () + { + var separator = (char)buffer [position]; + position++; + + var builder = new StringBuilder (); + for (int i = 0; CanReadMore (); i++) { + if (i > 0 && separator != 0) + builder.Append (separator); + + uint part = ReadCompressedUInt32 (); + if (part != 0) + builder.Append (reader.ReadUTF8StringBlob (part)); + } + + return builder.ToString (); + } + + public Collection ReadSequencePoints (Document document) + { + ReadCompressedUInt32 (); // local_sig_token + + if (document == null) + document = reader.GetDocument (ReadCompressedUInt32 ()); + + var offset = 0; + var start_line = 0; + var start_column = 0; + var first_non_hidden = true; + + //there's about 5 compressed int32's per sequenec points. we don't know exactly how many + //but let's take a conservative guess so we dont end up reallocating the sequence_points collection + //as it grows. + var bytes_remaining_for_sequencepoints = sig_length - (position - start); + var estimated_sequencepoint_amount = (int)bytes_remaining_for_sequencepoints / 5; + var sequence_points = new Collection (estimated_sequencepoint_amount); + + for (var i = 0; CanReadMore (); i++) { + var delta_il = (int)ReadCompressedUInt32 (); + if (i > 0 && delta_il == 0) { + document = reader.GetDocument (ReadCompressedUInt32 ()); + continue; + } + + offset += delta_il; + + var delta_lines = (int)ReadCompressedUInt32 (); + var delta_columns = delta_lines == 0 + ? (int)ReadCompressedUInt32 () + : ReadCompressedInt32 (); + + if (delta_lines == 0 && delta_columns == 0) { + sequence_points.Add (new SequencePoint (offset, document) { + StartLine = 0xfeefee, + EndLine = 0xfeefee, + StartColumn = 0, + EndColumn = 0, + }); + continue; + } + + if (first_non_hidden) { + start_line = (int)ReadCompressedUInt32 (); + start_column = (int)ReadCompressedUInt32 (); + } else { + start_line += ReadCompressedInt32 (); + start_column += ReadCompressedInt32 (); + } + + sequence_points.Add (new SequencePoint (offset, document) { + StartLine = start_line, + StartColumn = start_column, + EndLine = start_line + delta_lines, + EndColumn = start_column + delta_columns, + }); + first_non_hidden = false; + } + + return sequence_points; + } + + public bool CanReadMore () + { + return (position - start) < sig_length; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyReader.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyReader.cs.meta new file mode 100644 index 0000000..810883e --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d8fb74d16029713429e831cb3b5b0861 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyWriter.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyWriter.cs new file mode 100644 index 0000000..5d74689 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyWriter.cs @@ -0,0 +1,3336 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.Cil; +using MonoFN.Cecil.Metadata; +using MonoFN.Cecil.PE; +using MonoFN.Collections.Generic; +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Text; +using BlobIndex = System.UInt32; +using CodedRID = System.UInt32; +using GuidIndex = System.UInt32; +using RID = System.UInt32; +using RVA = System.UInt32; +using StringIndex = System.UInt32; + +namespace MonoFN.Cecil { + + using AssemblyRefRow = Row; + using AssemblyRow = Row; + using ClassLayoutRow = Row; + using ConstantRow = Row; + using CustomAttributeRow = Row; + using CustomDebugInformationRow = Row; + using DeclSecurityRow = Row; + using DocumentRow = Row; + using EventMapRow = Row; + using EventRow = Row; + using ExportedTypeRow = Row; + using FieldLayoutRow = Row; + using FieldMarshalRow = Row; + using FieldRow = Row; + using FieldRVARow = Row; + using FileRow = Row; + using GenericParamConstraintRow = Row; + using GenericParamRow = Row; + using ImplMapRow = Row; + using ImportScopeRow = Row; + using InterfaceImplRow = Row; + using LocalConstantRow = Row; + using LocalScopeRow = Row; + using LocalVariableRow = Row; + using ManifestResourceRow = Row; + using MemberRefRow = Row; + using MethodDebugInformationRow = Row; + using MethodImplRow = Row; + using MethodRow = Row; + using MethodSemanticsRow = Row; + using MethodSpecRow = Row; + using ModuleRow = Row; + using NestedClassRow = Row; + using ParamRow = Row; + using PropertyMapRow = Row; + using PropertyRow = Row; + using StateMachineMethodRow = Row; + using TypeDefRow = Row; + using TypeRefRow = Row; + + static class ModuleWriter { + + public static void WriteModule (ModuleDefinition module, Disposable stream, WriterParameters parameters) + { + using (stream) + Write (module, stream, parameters); + } + + static void Write (ModuleDefinition module, Disposable stream, WriterParameters parameters) + { + if ((module.Attributes & ModuleAttributes.ILOnly) == 0) + throw new NotSupportedException ("Writing mixed-mode assemblies is not supported"); + + if (module.HasImage && module.ReadingMode == ReadingMode.Deferred) { + var immediate_reader = new ImmediateModuleReader (module.Image); + immediate_reader.ReadModule (module, resolve_attributes: false); + immediate_reader.ReadSymbols (module); + } + + module.MetadataSystem.Clear (); + + if (module.symbol_reader != null) + module.symbol_reader.Dispose (); + + var name = module.assembly != null && module.kind != ModuleKind.NetModule ? module.assembly.Name : null; + var fq_name = stream.value.GetFileName (); + var timestamp = parameters.Timestamp ?? module.timestamp; + var symbol_writer_provider = parameters.SymbolWriterProvider; + + if (symbol_writer_provider == null && parameters.WriteSymbols) + symbol_writer_provider = new DefaultSymbolWriterProvider (); + + if (parameters.HasStrongNameKey && name != null) { + name.PublicKey = CryptoService.GetPublicKey (parameters); + module.Attributes |= ModuleAttributes.StrongNameSigned; + } + + if (parameters.DeterministicMvid) + module.Mvid = Guid.Empty; + + var metadata = new MetadataBuilder (module, fq_name, timestamp, symbol_writer_provider); + try { + module.metadata_builder = metadata; + + using (var symbol_writer = GetSymbolWriter (module, fq_name, symbol_writer_provider, parameters)) { + metadata.SetSymbolWriter (symbol_writer); + BuildMetadata (module, metadata); + + if (parameters.DeterministicMvid) + metadata.ComputeDeterministicMvid (); + + var writer = ImageWriter.CreateWriter (module, metadata, stream); + stream.value.SetLength (0); + writer.WriteImage (); + + if (parameters.HasStrongNameKey) + CryptoService.StrongName (stream.value, writer, parameters); + } + } + finally { + module.metadata_builder = null; + } + } + + static void BuildMetadata (ModuleDefinition module, MetadataBuilder metadata) + { + if (!module.HasImage) { + metadata.BuildMetadata (); + return; + } + + module.Read (metadata, (builder, _) => { + builder.BuildMetadata (); + return builder; + }); + } + + static ISymbolWriter GetSymbolWriter (ModuleDefinition module, string fq_name, ISymbolWriterProvider symbol_writer_provider, WriterParameters parameters) + { + if (symbol_writer_provider == null) + return null; + + if (parameters.SymbolStream != null) + return symbol_writer_provider.GetSymbolWriter (module, parameters.SymbolStream); + + return symbol_writer_provider.GetSymbolWriter (module, fq_name); + } + } + + abstract class MetadataTable { + + public abstract int Length { get; } + + public bool IsLarge { + get { return Length > ushort.MaxValue; } + } + + public abstract void Write (TableHeapBuffer buffer); + public abstract void Sort (); + } + + abstract class OneRowTable : MetadataTable where TRow : struct { + + internal TRow row; + + public sealed override int Length { + get { return 1; } + } + + public sealed override void Sort () + { + } + } + + abstract class MetadataTable : MetadataTable where TRow : struct { + + internal TRow [] rows = new TRow [2]; + internal int length; + + public sealed override int Length { + get { return length; } + } + + public int AddRow (TRow row) + { + if (rows.Length == length) + Grow (); + + rows [length++] = row; + return length; + } + + void Grow () + { + var rows = new TRow [this.rows.Length * 2]; + Array.Copy (this.rows, rows, this.rows.Length); + this.rows = rows; + } + + public override void Sort () + { + } + } + + abstract class SortedTable : MetadataTable, IComparer where TRow : struct { + + public sealed override void Sort () + { + MergeSort.Sort (rows, 0, this.length, this); + } + + protected static int Compare (uint x, uint y) + { + return x == y ? 0 : x > y ? 1 : -1; + } + + public abstract int Compare (TRow x, TRow y); + } + + sealed class ModuleTable : OneRowTable { + + public override void Write (TableHeapBuffer buffer) + { + buffer.WriteUInt16 (0); // Generation + buffer.WriteString (row.Col1); // Name + buffer.WriteGuid (row.Col2); // Mvid + buffer.WriteUInt16 (0); // EncId + buffer.WriteUInt16 (0); // EncBaseId + } + } + + sealed class TypeRefTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteCodedRID ( + rows [i].Col1, CodedIndex.ResolutionScope); // Scope + buffer.WriteString (rows [i].Col2); // Name + buffer.WriteString (rows [i].Col3); // Namespace + } + } + } + + sealed class TypeDefTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteUInt32 ((uint)rows [i].Col1); // Attributes + buffer.WriteString (rows [i].Col2); // Name + buffer.WriteString (rows [i].Col3); // Namespace + buffer.WriteCodedRID ( + rows [i].Col4, CodedIndex.TypeDefOrRef); // Extends + buffer.WriteRID (rows [i].Col5, Table.Field); // FieldList + buffer.WriteRID (rows [i].Col6, Table.Method); // MethodList + } + } + } + + sealed class FieldTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteUInt16 ((ushort)rows [i].Col1); // Attributes + buffer.WriteString (rows [i].Col2); // Name + buffer.WriteBlob (rows [i].Col3); // Signature + } + } + } + + sealed class MethodTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteUInt32 (rows [i].Col1); // RVA + buffer.WriteUInt16 ((ushort)rows [i].Col2); // ImplFlags + buffer.WriteUInt16 ((ushort)rows [i].Col3); // Flags + buffer.WriteString (rows [i].Col4); // Name + buffer.WriteBlob (rows [i].Col5); // Signature + buffer.WriteRID (rows [i].Col6, Table.Param); // ParamList + } + } + } + + sealed class ParamTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteUInt16 ((ushort)rows [i].Col1); // Attributes + buffer.WriteUInt16 (rows [i].Col2); // Sequence + buffer.WriteString (rows [i].Col3); // Name + } + } + } + + sealed class InterfaceImplTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteRID (rows [i].Col1, Table.TypeDef); // Class + buffer.WriteCodedRID (rows [i].Col2, CodedIndex.TypeDefOrRef); // Interface + } + } + + /*public override int Compare (InterfaceImplRow x, InterfaceImplRow y) + { + return (int) (x.Col1 == y.Col1 ? y.Col2 - x.Col2 : x.Col1 - y.Col1); + }*/ + } + + sealed class MemberRefTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteCodedRID (rows [i].Col1, CodedIndex.MemberRefParent); + buffer.WriteString (rows [i].Col2); + buffer.WriteBlob (rows [i].Col3); + } + } + } + + sealed class ConstantTable : SortedTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteUInt16 ((ushort)rows [i].Col1); + buffer.WriteCodedRID (rows [i].Col2, CodedIndex.HasConstant); + buffer.WriteBlob (rows [i].Col3); + } + } + + public override int Compare (ConstantRow x, ConstantRow y) + { + return Compare (x.Col2, y.Col2); + } + } + + sealed class CustomAttributeTable : SortedTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteCodedRID (rows [i].Col1, CodedIndex.HasCustomAttribute); // Parent + buffer.WriteCodedRID (rows [i].Col2, CodedIndex.CustomAttributeType); // Type + buffer.WriteBlob (rows [i].Col3); + } + } + + public override int Compare (CustomAttributeRow x, CustomAttributeRow y) + { + return Compare (x.Col1, y.Col1); + } + } + + sealed class FieldMarshalTable : SortedTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteCodedRID (rows [i].Col1, CodedIndex.HasFieldMarshal); + buffer.WriteBlob (rows [i].Col2); + } + } + + public override int Compare (FieldMarshalRow x, FieldMarshalRow y) + { + return Compare (x.Col1, y.Col1); + } + } + + sealed class DeclSecurityTable : SortedTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteUInt16 ((ushort)rows [i].Col1); + buffer.WriteCodedRID (rows [i].Col2, CodedIndex.HasDeclSecurity); + buffer.WriteBlob (rows [i].Col3); + } + } + + public override int Compare (DeclSecurityRow x, DeclSecurityRow y) + { + return Compare (x.Col2, y.Col2); + } + } + + sealed class ClassLayoutTable : SortedTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteUInt16 (rows [i].Col1); // PackingSize + buffer.WriteUInt32 (rows [i].Col2); // ClassSize + buffer.WriteRID (rows [i].Col3, Table.TypeDef); // Parent + } + } + + public override int Compare (ClassLayoutRow x, ClassLayoutRow y) + { + return Compare (x.Col3, y.Col3); + } + } + + sealed class FieldLayoutTable : SortedTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteUInt32 (rows [i].Col1); // Offset + buffer.WriteRID (rows [i].Col2, Table.Field); // Parent + } + } + + public override int Compare (FieldLayoutRow x, FieldLayoutRow y) + { + return Compare (x.Col2, y.Col2); + } + } + + sealed class StandAloneSigTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) + buffer.WriteBlob (rows [i]); + } + } + + sealed class EventMapTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteRID (rows [i].Col1, Table.TypeDef); // Parent + buffer.WriteRID (rows [i].Col2, Table.Event); // EventList + } + } + } + + sealed class EventTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteUInt16 ((ushort)rows [i].Col1); // Flags + buffer.WriteString (rows [i].Col2); // Name + buffer.WriteCodedRID (rows [i].Col3, CodedIndex.TypeDefOrRef); // EventType + } + } + } + + sealed class PropertyMapTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteRID (rows [i].Col1, Table.TypeDef); // Parent + buffer.WriteRID (rows [i].Col2, Table.Property); // PropertyList + } + } + } + + sealed class PropertyTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteUInt16 ((ushort)rows [i].Col1); // Flags + buffer.WriteString (rows [i].Col2); // Name + buffer.WriteBlob (rows [i].Col3); // Type + } + } + } + + sealed class MethodSemanticsTable : SortedTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteUInt16 ((ushort)rows [i].Col1); // Flags + buffer.WriteRID (rows [i].Col2, Table.Method); // Method + buffer.WriteCodedRID (rows [i].Col3, CodedIndex.HasSemantics); // Association + } + } + + public override int Compare (MethodSemanticsRow x, MethodSemanticsRow y) + { + return Compare (x.Col3, y.Col3); + } + } + + sealed class MethodImplTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteRID (rows [i].Col1, Table.TypeDef); // Class + buffer.WriteCodedRID (rows [i].Col2, CodedIndex.MethodDefOrRef); // MethodBody + buffer.WriteCodedRID (rows [i].Col3, CodedIndex.MethodDefOrRef); // MethodDeclaration + } + } + } + + sealed class ModuleRefTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) + buffer.WriteString (rows [i]); // Name + } + } + + sealed class TypeSpecTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) + buffer.WriteBlob (rows [i]); // Signature + } + } + + sealed class ImplMapTable : SortedTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteUInt16 ((ushort)rows [i].Col1); // Flags + buffer.WriteCodedRID (rows [i].Col2, CodedIndex.MemberForwarded); // MemberForwarded + buffer.WriteString (rows [i].Col3); // ImportName + buffer.WriteRID (rows [i].Col4, Table.ModuleRef); // ImportScope + } + } + + public override int Compare (ImplMapRow x, ImplMapRow y) + { + return Compare (x.Col2, y.Col2); + } + } + + sealed class FieldRVATable : SortedTable { + + internal int position; + + public override void Write (TableHeapBuffer buffer) + { + position = buffer.position; + for (int i = 0; i < length; i++) { + buffer.WriteUInt32 (rows [i].Col1); // RVA + buffer.WriteRID (rows [i].Col2, Table.Field); // Field + } + } + + public override int Compare (FieldRVARow x, FieldRVARow y) + { + return Compare (x.Col2, y.Col2); + } + } + + sealed class AssemblyTable : OneRowTable { + + public override void Write (TableHeapBuffer buffer) + { + buffer.WriteUInt32 ((uint)row.Col1); // AssemblyHashAlgorithm + buffer.WriteUInt16 (row.Col2); // MajorVersion + buffer.WriteUInt16 (row.Col3); // MinorVersion + buffer.WriteUInt16 (row.Col4); // Build + buffer.WriteUInt16 (row.Col5); // Revision + buffer.WriteUInt32 ((uint)row.Col6); // Flags + buffer.WriteBlob (row.Col7); // PublicKey + buffer.WriteString (row.Col8); // Name + buffer.WriteString (row.Col9); // Culture + } + } + + sealed class AssemblyRefTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteUInt16 (rows [i].Col1); // MajorVersion + buffer.WriteUInt16 (rows [i].Col2); // MinorVersion + buffer.WriteUInt16 (rows [i].Col3); // Build + buffer.WriteUInt16 (rows [i].Col4); // Revision + buffer.WriteUInt32 ((uint)rows [i].Col5); // Flags + buffer.WriteBlob (rows [i].Col6); // PublicKeyOrToken + buffer.WriteString (rows [i].Col7); // Name + buffer.WriteString (rows [i].Col8); // Culture + buffer.WriteBlob (rows [i].Col9); // Hash + } + } + } + + sealed class FileTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteUInt32 ((uint)rows [i].Col1); + buffer.WriteString (rows [i].Col2); + buffer.WriteBlob (rows [i].Col3); + } + } + } + + sealed class ExportedTypeTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteUInt32 ((uint)rows [i].Col1); + buffer.WriteUInt32 (rows [i].Col2); + buffer.WriteString (rows [i].Col3); + buffer.WriteString (rows [i].Col4); + buffer.WriteCodedRID (rows [i].Col5, CodedIndex.Implementation); + } + } + } + + sealed class ManifestResourceTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteUInt32 (rows [i].Col1); + buffer.WriteUInt32 ((uint)rows [i].Col2); + buffer.WriteString (rows [i].Col3); + buffer.WriteCodedRID (rows [i].Col4, CodedIndex.Implementation); + } + } + } + + sealed class NestedClassTable : SortedTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteRID (rows [i].Col1, Table.TypeDef); // NestedClass + buffer.WriteRID (rows [i].Col2, Table.TypeDef); // EnclosingClass + } + } + + public override int Compare (NestedClassRow x, NestedClassRow y) + { + return Compare (x.Col1, y.Col1); + } + } + + sealed class GenericParamTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteUInt16 (rows [i].Col1); // Number + buffer.WriteUInt16 ((ushort)rows [i].Col2); // Flags + buffer.WriteCodedRID (rows [i].Col3, CodedIndex.TypeOrMethodDef); // Owner + buffer.WriteString (rows [i].Col4); // Name + } + } + } + + sealed class MethodSpecTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteCodedRID (rows [i].Col1, CodedIndex.MethodDefOrRef); // Method + buffer.WriteBlob (rows [i].Col2); // Instantiation + } + } + } + + sealed class GenericParamConstraintTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteRID (rows [i].Col1, Table.GenericParam); // Owner + buffer.WriteCodedRID (rows [i].Col2, CodedIndex.TypeDefOrRef); // Constraint + } + } + } + + sealed class DocumentTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteBlob (rows [i].Col1); // Name + buffer.WriteGuid (rows [i].Col2); // HashAlgorithm + buffer.WriteBlob (rows [i].Col3); // Hash + buffer.WriteGuid (rows [i].Col4); // Language + } + } + } + + sealed class MethodDebugInformationTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteRID (rows [i].Col1, Table.Document); // Document + buffer.WriteBlob (rows [i].Col2); // SequencePoints + } + } + } + + sealed class LocalScopeTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteRID (rows [i].Col1, Table.Method); // Method + buffer.WriteRID (rows [i].Col2, Table.ImportScope); // ImportScope + buffer.WriteRID (rows [i].Col3, Table.LocalVariable); // VariableList + buffer.WriteRID (rows [i].Col4, Table.LocalConstant); // ConstantList + buffer.WriteUInt32 (rows [i].Col5); // StartOffset + buffer.WriteUInt32 (rows [i].Col6); // Length + } + } + } + + sealed class LocalVariableTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteUInt16 ((ushort)rows [i].Col1); // Attributes + buffer.WriteUInt16 (rows [i].Col2); // Index + buffer.WriteString (rows [i].Col3); // Name + } + } + } + + sealed class LocalConstantTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteString (rows [i].Col1); // Name + buffer.WriteBlob (rows [i].Col2); // Signature + } + } + } + + sealed class ImportScopeTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteRID (rows [i].Col1, Table.ImportScope); // Parent + buffer.WriteBlob (rows [i].Col2); // Imports + } + } + } + + sealed class StateMachineMethodTable : MetadataTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteRID (rows [i].Col1, Table.Method); // MoveNextMethod + buffer.WriteRID (rows [i].Col2, Table.Method); // KickoffMethod + } + } + } + + sealed class CustomDebugInformationTable : SortedTable { + + public override void Write (TableHeapBuffer buffer) + { + for (int i = 0; i < length; i++) { + buffer.WriteCodedRID (rows [i].Col1, CodedIndex.HasCustomDebugInformation); // Parent + buffer.WriteGuid (rows [i].Col2); // Kind + buffer.WriteBlob (rows [i].Col3); // Value + } + } + + public override int Compare (CustomDebugInformationRow x, CustomDebugInformationRow y) + { + return Compare (x.Col1, y.Col1); + } + } + + sealed class MetadataBuilder { + + readonly internal ModuleDefinition module; + readonly internal ISymbolWriterProvider symbol_writer_provider; + internal ISymbolWriter symbol_writer; + readonly internal TextMap text_map; + readonly internal string fq_name; + readonly internal uint timestamp; + + readonly Dictionary type_ref_map; + readonly Dictionary type_spec_map; + readonly Dictionary member_ref_map; + readonly Dictionary method_spec_map; + readonly Collection generic_parameters; + + readonly internal CodeWriter code; + readonly internal DataBuffer data; + readonly internal ResourceBuffer resources; + readonly internal StringHeapBuffer string_heap; + readonly internal GuidHeapBuffer guid_heap; + readonly internal UserStringHeapBuffer user_string_heap; + readonly internal BlobHeapBuffer blob_heap; + readonly internal TableHeapBuffer table_heap; + readonly internal PdbHeapBuffer pdb_heap; + + internal MetadataToken entry_point; + + internal RID type_rid = 1; + internal RID field_rid = 1; + internal RID method_rid = 1; + internal RID param_rid = 1; + internal RID property_rid = 1; + internal RID event_rid = 1; + internal RID local_variable_rid = 1; + internal RID local_constant_rid = 1; + + readonly TypeRefTable type_ref_table; + readonly TypeDefTable type_def_table; + readonly FieldTable field_table; + readonly MethodTable method_table; + readonly ParamTable param_table; + readonly InterfaceImplTable iface_impl_table; + readonly MemberRefTable member_ref_table; + readonly ConstantTable constant_table; + readonly CustomAttributeTable custom_attribute_table; + readonly DeclSecurityTable declsec_table; + readonly StandAloneSigTable standalone_sig_table; + readonly EventMapTable event_map_table; + readonly EventTable event_table; + readonly PropertyMapTable property_map_table; + readonly PropertyTable property_table; + readonly TypeSpecTable typespec_table; + readonly MethodSpecTable method_spec_table; + + internal MetadataBuilder metadata_builder; + + readonly DocumentTable document_table; + readonly MethodDebugInformationTable method_debug_information_table; + readonly LocalScopeTable local_scope_table; + readonly LocalVariableTable local_variable_table; + readonly LocalConstantTable local_constant_table; + readonly ImportScopeTable import_scope_table; + readonly StateMachineMethodTable state_machine_method_table; + readonly CustomDebugInformationTable custom_debug_information_table; + + readonly Dictionary import_scope_map; + readonly Dictionary document_map; + + public MetadataBuilder (ModuleDefinition module, string fq_name, uint timestamp, ISymbolWriterProvider symbol_writer_provider) + { + this.module = module; + this.text_map = CreateTextMap (); + this.fq_name = fq_name; + this.timestamp = timestamp; + this.symbol_writer_provider = symbol_writer_provider; + + this.code = new CodeWriter (this); + this.data = new DataBuffer (); + this.resources = new ResourceBuffer (); + this.string_heap = new StringHeapBuffer (); + this.guid_heap = new GuidHeapBuffer (); + this.user_string_heap = new UserStringHeapBuffer (); + this.blob_heap = new BlobHeapBuffer (); + this.table_heap = new TableHeapBuffer (module, this); + + this.type_ref_table = GetTable (Table.TypeRef); + this.type_def_table = GetTable (Table.TypeDef); + this.field_table = GetTable (Table.Field); + this.method_table = GetTable (Table.Method); + this.param_table = GetTable (Table.Param); + this.iface_impl_table = GetTable (Table.InterfaceImpl); + this.member_ref_table = GetTable (Table.MemberRef); + this.constant_table = GetTable (Table.Constant); + this.custom_attribute_table = GetTable (Table.CustomAttribute); + this.declsec_table = GetTable (Table.DeclSecurity); + this.standalone_sig_table = GetTable (Table.StandAloneSig); + this.event_map_table = GetTable (Table.EventMap); + this.event_table = GetTable (Table.Event); + this.property_map_table = GetTable (Table.PropertyMap); + this.property_table = GetTable (Table.Property); + this.typespec_table = GetTable (Table.TypeSpec); + this.method_spec_table = GetTable (Table.MethodSpec); + + var row_equality_comparer = new RowEqualityComparer (); + type_ref_map = new Dictionary (row_equality_comparer); + type_spec_map = new Dictionary (); + member_ref_map = new Dictionary (row_equality_comparer); + method_spec_map = new Dictionary (row_equality_comparer); + generic_parameters = new Collection (); + + this.document_table = GetTable (Table.Document); + this.method_debug_information_table = GetTable (Table.MethodDebugInformation); + this.local_scope_table = GetTable (Table.LocalScope); + this.local_variable_table = GetTable (Table.LocalVariable); + this.local_constant_table = GetTable (Table.LocalConstant); + this.import_scope_table = GetTable (Table.ImportScope); + this.state_machine_method_table = GetTable (Table.StateMachineMethod); + this.custom_debug_information_table = GetTable (Table.CustomDebugInformation); + + this.document_map = new Dictionary (StringComparer.Ordinal); + this.import_scope_map = new Dictionary (row_equality_comparer); + } + + public MetadataBuilder (ModuleDefinition module, PortablePdbWriterProvider writer_provider) + { + this.module = module; + this.text_map = new TextMap (); + this.symbol_writer_provider = writer_provider; + + this.string_heap = new StringHeapBuffer (); + this.guid_heap = new GuidHeapBuffer (); + this.user_string_heap = new UserStringHeapBuffer (); + this.blob_heap = new BlobHeapBuffer (); + this.table_heap = new TableHeapBuffer (module, this); + this.pdb_heap = new PdbHeapBuffer (); + + this.document_table = GetTable (Table.Document); + this.method_debug_information_table = GetTable (Table.MethodDebugInformation); + this.local_scope_table = GetTable (Table.LocalScope); + this.local_variable_table = GetTable (Table.LocalVariable); + this.local_constant_table = GetTable (Table.LocalConstant); + this.import_scope_table = GetTable (Table.ImportScope); + this.state_machine_method_table = GetTable (Table.StateMachineMethod); + this.custom_debug_information_table = GetTable (Table.CustomDebugInformation); + + var row_equality_comparer = new RowEqualityComparer (); + + this.document_map = new Dictionary (); + this.import_scope_map = new Dictionary (row_equality_comparer); + } + + public void SetSymbolWriter (ISymbolWriter writer) + { + symbol_writer = writer; + + if (symbol_writer == null && module.HasImage && module.Image.HasDebugTables ()) + symbol_writer = new PortablePdbWriter (this, module); + } + + TextMap CreateTextMap () + { + var map = new TextMap (); + map.AddMap (TextSegment.ImportAddressTable, module.Architecture == TargetArchitecture.I386 ? 8 : 0); + map.AddMap (TextSegment.CLIHeader, 0x48, 8); + return map; + } + + TTable GetTable (Table table) where TTable : MetadataTable, new() + { + return table_heap.GetTable (table); + } + + uint GetStringIndex (string @string) + { + if (string.IsNullOrEmpty (@string)) + return 0; + + return string_heap.GetStringIndex (@string); + } + + uint GetGuidIndex (Guid guid) + { + return guid_heap.GetGuidIndex (guid); + } + + uint GetBlobIndex (ByteBuffer blob) + { + if (blob.length == 0) + return 0; + + return blob_heap.GetBlobIndex (blob); + } + + uint GetBlobIndex (byte [] blob) + { + if (blob.IsNullOrEmpty ()) + return 0; + + return GetBlobIndex (new ByteBuffer (blob)); + } + + public void BuildMetadata () + { + BuildModule (); + + table_heap.string_offsets = string_heap.WriteStrings (); + table_heap.ComputeTableInformations (); + table_heap.WriteTableHeap (); + } + + void BuildModule () + { + var table = GetTable (Table.Module); + table.row.Col1 = GetStringIndex (module.Name); + table.row.Col2 = GetGuidIndex (module.Mvid); + + var assembly = module.Assembly; + + if (module.kind != ModuleKind.NetModule && assembly != null) + BuildAssembly (); + + if (module.HasAssemblyReferences) + AddAssemblyReferences (); + + if (module.HasModuleReferences) + AddModuleReferences (); + + if (module.HasResources) + AddResources (); + + if (module.HasExportedTypes) + AddExportedTypes (); + + BuildTypes (); + + if (module.kind != ModuleKind.NetModule && assembly != null) { + if (assembly.HasCustomAttributes) + AddCustomAttributes (assembly); + + if (assembly.HasSecurityDeclarations) + AddSecurityDeclarations (assembly); + } + + if (module.HasCustomAttributes) + AddCustomAttributes (module); + + if (module.EntryPoint != null) + entry_point = LookupToken (module.EntryPoint); + } + + void BuildAssembly () + { + var assembly = module.Assembly; + var name = assembly.Name; + + var table = GetTable (Table.Assembly); + + table.row = new AssemblyRow ( + name.HashAlgorithm, + (ushort)name.Version.Major, + (ushort)name.Version.Minor, + (ushort)name.Version.Build, + (ushort)name.Version.Revision, + name.Attributes, + GetBlobIndex (name.PublicKey), + GetStringIndex (name.Name), + GetStringIndex (name.Culture)); + + if (assembly.Modules.Count > 1) + BuildModules (); + } + + void BuildModules () + { + var modules = this.module.Assembly.Modules; + var table = GetTable (Table.File); + + for (int i = 0; i < modules.Count; i++) { + var module = modules [i]; + if (module.IsMain) + continue; + +#if NET_CORE + throw new NotSupportedException (); +#else + var parameters = new WriterParameters { + SymbolWriterProvider = symbol_writer_provider, + }; + + var file_name = GetModuleFileName (module.Name); + module.Write (file_name, parameters); + + var hash = CryptoService.ComputeHash (file_name); + + table.AddRow (new FileRow ( + FileAttributes.ContainsMetaData, + GetStringIndex (module.Name), + GetBlobIndex (hash))); +#endif + } + } + +#if !NET_CORE + string GetModuleFileName (string name) + { + if (string.IsNullOrEmpty (name)) + throw new NotSupportedException (); + + var path = Path.GetDirectoryName (fq_name); + return Path.Combine (path, name); + } +#endif + + void AddAssemblyReferences () + { + var references = module.AssemblyReferences; + var table = GetTable (Table.AssemblyRef); + + if (module.IsWindowsMetadata ()) + module.Projections.RemoveVirtualReferences (references); + + for (int i = 0; i < references.Count; i++) { + var reference = references [i]; + + var key_or_token = reference.PublicKey.IsNullOrEmpty () + ? reference.PublicKeyToken + : reference.PublicKey; + + var version = reference.Version; + + var rid = table.AddRow (new AssemblyRefRow ( + (ushort)version.Major, + (ushort)version.Minor, + (ushort)version.Build, + (ushort)version.Revision, + reference.Attributes, + GetBlobIndex (key_or_token), + GetStringIndex (reference.Name), + GetStringIndex (reference.Culture), + GetBlobIndex (reference.Hash))); + + reference.token = new MetadataToken (TokenType.AssemblyRef, rid); + } + + if (module.IsWindowsMetadata ()) + module.Projections.AddVirtualReferences (references); + } + + void AddModuleReferences () + { + var references = module.ModuleReferences; + var table = GetTable (Table.ModuleRef); + + for (int i = 0; i < references.Count; i++) { + var reference = references [i]; + + reference.token = new MetadataToken ( + TokenType.ModuleRef, + table.AddRow (GetStringIndex (reference.Name))); + } + } + + void AddResources () + { + var resources = module.Resources; + var table = GetTable (Table.ManifestResource); + + for (int i = 0; i < resources.Count; i++) { + var resource = resources [i]; + + var row = new ManifestResourceRow ( + 0, + resource.Attributes, + GetStringIndex (resource.Name), + 0); + + switch (resource.ResourceType) { + case ResourceType.Embedded: + row.Col1 = AddEmbeddedResource ((EmbeddedResource)resource); + break; + case ResourceType.Linked: + row.Col4 = CodedIndex.Implementation.CompressMetadataToken ( + new MetadataToken ( + TokenType.File, + AddLinkedResource ((LinkedResource)resource))); + break; + case ResourceType.AssemblyLinked: + row.Col4 = CodedIndex.Implementation.CompressMetadataToken ( + ((AssemblyLinkedResource)resource).Assembly.MetadataToken); + break; + default: + throw new NotSupportedException (); + } + + table.AddRow (row); + } + } + + uint AddLinkedResource (LinkedResource resource) + { + var table = GetTable (Table.File); + var hash = resource.Hash; + + if (hash.IsNullOrEmpty ()) + hash = CryptoService.ComputeHash (resource.File); + + return (uint)table.AddRow (new FileRow ( + FileAttributes.ContainsNoMetaData, + GetStringIndex (resource.File), + GetBlobIndex (hash))); + } + + uint AddEmbeddedResource (EmbeddedResource resource) + { + return resources.AddResource (resource.GetResourceData ()); + } + + void AddExportedTypes () + { + var exported_types = module.ExportedTypes; + var table = GetTable (Table.ExportedType); + + for (int i = 0; i < exported_types.Count; i++) { + var exported_type = exported_types [i]; + + var rid = table.AddRow (new ExportedTypeRow ( + exported_type.Attributes, + (uint)exported_type.Identifier, + GetStringIndex (exported_type.Name), + GetStringIndex (exported_type.Namespace), + MakeCodedRID (GetExportedTypeScope (exported_type), CodedIndex.Implementation))); + + exported_type.token = new MetadataToken (TokenType.ExportedType, rid); + } + } + + MetadataToken GetExportedTypeScope (ExportedType exported_type) + { + if (exported_type.DeclaringType != null) + return exported_type.DeclaringType.MetadataToken; + + var scope = exported_type.Scope; + switch (scope.MetadataToken.TokenType) { + case TokenType.AssemblyRef: + return scope.MetadataToken; + case TokenType.ModuleRef: + var file_table = GetTable (Table.File); + for (int i = 0; i < file_table.length; i++) + if (file_table.rows [i].Col2 == GetStringIndex (scope.Name)) + return new MetadataToken (TokenType.File, i + 1); + + break; + } + + throw new NotSupportedException (); + } + + void BuildTypes () + { + if (!module.HasTypes) + return; + + AttachTokens (); + AddTypes (); + AddGenericParameters (); + } + + void AttachTokens () + { + var types = module.Types; + + for (int i = 0; i < types.Count; i++) + AttachTypeToken (types [i]); + } + + void AttachTypeToken (TypeDefinition type) + { + var treatment = WindowsRuntimeProjections.RemoveProjection (type); + + type.token = new MetadataToken (TokenType.TypeDef, type_rid++); + type.fields_range.Start = field_rid; + type.methods_range.Start = method_rid; + + if (type.HasFields) + AttachFieldsToken (type); + + if (type.HasMethods) + AttachMethodsToken (type); + + if (type.HasNestedTypes) + AttachNestedTypesToken (type); + + WindowsRuntimeProjections.ApplyProjection (type, treatment); + } + + void AttachNestedTypesToken (TypeDefinition type) + { + var nested_types = type.NestedTypes; + for (int i = 0; i < nested_types.Count; i++) + AttachTypeToken (nested_types [i]); + } + + void AttachFieldsToken (TypeDefinition type) + { + var fields = type.Fields; + type.fields_range.Length = (uint)fields.Count; + for (int i = 0; i < fields.Count; i++) + fields [i].token = new MetadataToken (TokenType.Field, field_rid++); + } + + void AttachMethodsToken (TypeDefinition type) + { + var methods = type.Methods; + type.methods_range.Length = (uint)methods.Count; + for (int i = 0; i < methods.Count; i++) + methods [i].token = new MetadataToken (TokenType.Method, method_rid++); + } + + MetadataToken GetTypeToken (TypeReference type) + { + if (type == null) + return MetadataToken.Zero; + + if (type.IsDefinition) + return type.token; + + if (type.IsTypeSpecification ()) + return GetTypeSpecToken (type); + + return GetTypeRefToken (type); + } + + MetadataToken GetTypeSpecToken (TypeReference type) + { + var row = GetBlobIndex (GetTypeSpecSignature (type)); + + MetadataToken token; + if (type_spec_map.TryGetValue (row, out token)) + return token; + + return AddTypeSpecification (type, row); + } + + MetadataToken AddTypeSpecification (TypeReference type, uint row) + { + type.token = new MetadataToken (TokenType.TypeSpec, typespec_table.AddRow (row)); + + var token = type.token; + type_spec_map.Add (row, token); + return token; + } + + MetadataToken GetTypeRefToken (TypeReference type) + { + var projection = WindowsRuntimeProjections.RemoveProjection (type); + + var row = CreateTypeRefRow (type); + + MetadataToken token; + if (!type_ref_map.TryGetValue (row, out token)) + token = AddTypeReference (type, row); + + WindowsRuntimeProjections.ApplyProjection (type, projection); + + return token; + } + + TypeRefRow CreateTypeRefRow (TypeReference type) + { + var scope_token = GetScopeToken (type); + + return new TypeRefRow ( + MakeCodedRID (scope_token, CodedIndex.ResolutionScope), + GetStringIndex (type.Name), + GetStringIndex (type.Namespace)); + } + + MetadataToken GetScopeToken (TypeReference type) + { + if (type.IsNested) + return GetTypeRefToken (type.DeclaringType); + + var scope = type.Scope; + + if (scope == null) + return MetadataToken.Zero; + + return scope.MetadataToken; + } + + static CodedRID MakeCodedRID (IMetadataTokenProvider provider, CodedIndex index) + { + return MakeCodedRID (provider.MetadataToken, index); + } + + static CodedRID MakeCodedRID (MetadataToken token, CodedIndex index) + { + return index.CompressMetadataToken (token); + } + + MetadataToken AddTypeReference (TypeReference type, TypeRefRow row) + { + type.token = new MetadataToken (TokenType.TypeRef, type_ref_table.AddRow (row)); + + var token = type.token; + type_ref_map.Add (row, token); + return token; + } + + void AddTypes () + { + var types = module.Types; + + for (int i = 0; i < types.Count; i++) + AddType (types [i]); + } + + void AddType (TypeDefinition type) + { + var treatment = WindowsRuntimeProjections.RemoveProjection (type); + + type_def_table.AddRow (new TypeDefRow ( + type.Attributes, + GetStringIndex (type.Name), + GetStringIndex (type.Namespace), + MakeCodedRID (GetTypeToken (type.BaseType), CodedIndex.TypeDefOrRef), + type.fields_range.Start, + type.methods_range.Start)); + + if (type.HasGenericParameters) + AddGenericParameters (type); + + if (type.HasInterfaces) + AddInterfaces (type); + + if (type.HasLayoutInfo) + AddLayoutInfo (type); + + if (type.HasFields) + AddFields (type); + + if (type.HasMethods) + AddMethods (type); + + if (type.HasProperties) + AddProperties (type); + + if (type.HasEvents) + AddEvents (type); + + if (type.HasCustomAttributes) + AddCustomAttributes (type); + + if (type.HasSecurityDeclarations) + AddSecurityDeclarations (type); + + if (type.HasNestedTypes) + AddNestedTypes (type); + + WindowsRuntimeProjections.ApplyProjection (type, treatment); + } + + void AddGenericParameters (IGenericParameterProvider owner) + { + var parameters = owner.GenericParameters; + + for (int i = 0; i < parameters.Count; i++) + generic_parameters.Add (parameters [i]); + } + + sealed class GenericParameterComparer : IComparer { + + public int Compare (GenericParameter a, GenericParameter b) + { + var a_owner = MakeCodedRID (a.Owner, CodedIndex.TypeOrMethodDef); + var b_owner = MakeCodedRID (b.Owner, CodedIndex.TypeOrMethodDef); + if (a_owner == b_owner) { + var a_pos = a.Position; + var b_pos = b.Position; + return a_pos == b_pos ? 0 : a_pos > b_pos ? 1 : -1; + } + + return a_owner > b_owner ? 1 : -1; + } + } + + void AddGenericParameters () + { + var items = this.generic_parameters.items; + var size = this.generic_parameters.size; + Array.Sort (items, 0, size, new GenericParameterComparer ()); + + var generic_param_table = GetTable (Table.GenericParam); + var generic_param_constraint_table = GetTable (Table.GenericParamConstraint); + + for (int i = 0; i < size; i++) { + var generic_parameter = items [i]; + + var rid = generic_param_table.AddRow (new GenericParamRow ( + (ushort)generic_parameter.Position, + generic_parameter.Attributes, + MakeCodedRID (generic_parameter.Owner, CodedIndex.TypeOrMethodDef), + GetStringIndex (generic_parameter.Name))); + + generic_parameter.token = new MetadataToken (TokenType.GenericParam, rid); + + if (generic_parameter.HasConstraints) + AddConstraints (generic_parameter, generic_param_constraint_table); + + if (generic_parameter.HasCustomAttributes) + AddCustomAttributes (generic_parameter); + } + } + + void AddConstraints (GenericParameter generic_parameter, GenericParamConstraintTable table) + { + var constraints = generic_parameter.Constraints; + + var gp_rid = generic_parameter.token.RID; + + for (int i = 0; i < constraints.Count; i++) { + var constraint = constraints [i]; + + var rid = table.AddRow (new GenericParamConstraintRow ( + gp_rid, + MakeCodedRID (GetTypeToken (constraint.ConstraintType), CodedIndex.TypeDefOrRef))); + + constraint.token = new MetadataToken (TokenType.GenericParamConstraint, rid); + + if (constraint.HasCustomAttributes) + AddCustomAttributes (constraint); + } + } + + void AddInterfaces (TypeDefinition type) + { + var interfaces = type.Interfaces; + var type_rid = type.token.RID; + + for (int i = 0; i < interfaces.Count; i++) { + var iface_impl = interfaces [i]; + + var rid = iface_impl_table.AddRow (new InterfaceImplRow ( + type_rid, + MakeCodedRID (GetTypeToken (iface_impl.InterfaceType), CodedIndex.TypeDefOrRef))); + + iface_impl.token = new MetadataToken (TokenType.InterfaceImpl, rid); + + if (iface_impl.HasCustomAttributes) + AddCustomAttributes (iface_impl); + } + } + + void AddLayoutInfo (TypeDefinition type) + { + var table = GetTable (Table.ClassLayout); + + table.AddRow (new ClassLayoutRow ( + (ushort)type.PackingSize, + (uint)type.ClassSize, + type.token.RID)); + } + + void AddNestedTypes (TypeDefinition type) + { + var nested_types = type.NestedTypes; + var nested_table = GetTable (Table.NestedClass); + + for (int i = 0; i < nested_types.Count; i++) { + var nested = nested_types [i]; + AddType (nested); + nested_table.AddRow (new NestedClassRow (nested.token.RID, type.token.RID)); + } + } + + void AddFields (TypeDefinition type) + { + var fields = type.Fields; + + for (int i = 0; i < fields.Count; i++) + AddField (fields [i]); + } + + void AddField (FieldDefinition field) + { + var projection = WindowsRuntimeProjections.RemoveProjection (field); + + field_table.AddRow (new FieldRow ( + field.Attributes, + GetStringIndex (field.Name), + GetBlobIndex (GetFieldSignature (field)))); + + if (!field.InitialValue.IsNullOrEmpty ()) + AddFieldRVA (field); + + if (field.HasLayoutInfo) + AddFieldLayout (field); + + if (field.HasCustomAttributes) + AddCustomAttributes (field); + + if (field.HasConstant) + AddConstant (field, field.FieldType); + + if (field.HasMarshalInfo) + AddMarshalInfo (field); + + WindowsRuntimeProjections.ApplyProjection (field, projection); + } + + void AddFieldRVA (FieldDefinition field) + { + var table = GetTable (Table.FieldRVA); + table.AddRow (new FieldRVARow ( + data.AddData (field.InitialValue), + field.token.RID)); + } + + void AddFieldLayout (FieldDefinition field) + { + var table = GetTable (Table.FieldLayout); + table.AddRow (new FieldLayoutRow ((uint)field.Offset, field.token.RID)); + } + + void AddMethods (TypeDefinition type) + { + var methods = type.Methods; + + for (int i = 0; i < methods.Count; i++) + AddMethod (methods [i]); + } + + void AddMethod (MethodDefinition method) + { + var projection = WindowsRuntimeProjections.RemoveProjection (method); + + method_table.AddRow (new MethodRow ( + method.HasBody ? code.WriteMethodBody (method) : 0, + method.ImplAttributes, + method.Attributes, + GetStringIndex (method.Name), + GetBlobIndex (GetMethodSignature (method)), + param_rid)); + + AddParameters (method); + + if (method.HasGenericParameters) + AddGenericParameters (method); + + if (method.IsPInvokeImpl) + AddPInvokeInfo (method); + + if (method.HasCustomAttributes) + AddCustomAttributes (method); + + if (method.HasSecurityDeclarations) + AddSecurityDeclarations (method); + + if (method.HasOverrides) + AddOverrides (method); + + WindowsRuntimeProjections.ApplyProjection (method, projection); + } + + void AddParameters (MethodDefinition method) + { + var return_parameter = method.MethodReturnType.parameter; + + if (return_parameter != null && RequiresParameterRow (return_parameter)) + AddParameter (0, return_parameter, param_table); + + if (!method.HasParameters) + return; + + var parameters = method.Parameters; + + for (int i = 0; i < parameters.Count; i++) { + var parameter = parameters [i]; + if (!RequiresParameterRow (parameter)) + continue; + + AddParameter ((ushort)(i + 1), parameter, param_table); + } + } + + void AddPInvokeInfo (MethodDefinition method) + { + var pinvoke = method.PInvokeInfo; + if (pinvoke == null) + return; + + var table = GetTable (Table.ImplMap); + table.AddRow (new ImplMapRow ( + pinvoke.Attributes, + MakeCodedRID (method, CodedIndex.MemberForwarded), + GetStringIndex (pinvoke.EntryPoint), + pinvoke.Module.MetadataToken.RID)); + } + + void AddOverrides (MethodDefinition method) + { + var overrides = method.Overrides; + var table = GetTable (Table.MethodImpl); + + for (int i = 0; i < overrides.Count; i++) { + table.AddRow (new MethodImplRow ( + method.DeclaringType.token.RID, + MakeCodedRID (method, CodedIndex.MethodDefOrRef), + MakeCodedRID (LookupToken (overrides [i]), CodedIndex.MethodDefOrRef))); + } + } + + static bool RequiresParameterRow (ParameterDefinition parameter) + { + return !string.IsNullOrEmpty (parameter.Name) + || parameter.Attributes != ParameterAttributes.None + || parameter.HasMarshalInfo + || parameter.HasConstant + || parameter.HasCustomAttributes; + } + + void AddParameter (ushort sequence, ParameterDefinition parameter, ParamTable table) + { + table.AddRow (new ParamRow ( + parameter.Attributes, + sequence, + GetStringIndex (parameter.Name))); + + parameter.token = new MetadataToken (TokenType.Param, param_rid++); + + if (parameter.HasCustomAttributes) + AddCustomAttributes (parameter); + + if (parameter.HasConstant) + AddConstant (parameter, parameter.ParameterType); + + if (parameter.HasMarshalInfo) + AddMarshalInfo (parameter); + } + + void AddMarshalInfo (IMarshalInfoProvider owner) + { + var table = GetTable (Table.FieldMarshal); + + table.AddRow (new FieldMarshalRow ( + MakeCodedRID (owner, CodedIndex.HasFieldMarshal), + GetBlobIndex (GetMarshalInfoSignature (owner)))); + } + + void AddProperties (TypeDefinition type) + { + var properties = type.Properties; + + property_map_table.AddRow (new PropertyMapRow (type.token.RID, property_rid)); + + for (int i = 0; i < properties.Count; i++) + AddProperty (properties [i]); + } + + void AddProperty (PropertyDefinition property) + { + property_table.AddRow (new PropertyRow ( + property.Attributes, + GetStringIndex (property.Name), + GetBlobIndex (GetPropertySignature (property)))); + property.token = new MetadataToken (TokenType.Property, property_rid++); + + var method = property.GetMethod; + if (method != null) + AddSemantic (MethodSemanticsAttributes.Getter, property, method); + + method = property.SetMethod; + if (method != null) + AddSemantic (MethodSemanticsAttributes.Setter, property, method); + + if (property.HasOtherMethods) + AddOtherSemantic (property, property.OtherMethods); + + if (property.HasCustomAttributes) + AddCustomAttributes (property); + + if (property.HasConstant) + AddConstant (property, property.PropertyType); + } + + void AddOtherSemantic (IMetadataTokenProvider owner, Collection others) + { + for (int i = 0; i < others.Count; i++) + AddSemantic (MethodSemanticsAttributes.Other, owner, others [i]); + } + + void AddEvents (TypeDefinition type) + { + var events = type.Events; + + event_map_table.AddRow (new EventMapRow (type.token.RID, event_rid)); + + for (int i = 0; i < events.Count; i++) + AddEvent (events [i]); + } + + void AddEvent (EventDefinition @event) + { + event_table.AddRow (new EventRow ( + @event.Attributes, + GetStringIndex (@event.Name), + MakeCodedRID (GetTypeToken (@event.EventType), CodedIndex.TypeDefOrRef))); + @event.token = new MetadataToken (TokenType.Event, event_rid++); + + var method = @event.AddMethod; + if (method != null) + AddSemantic (MethodSemanticsAttributes.AddOn, @event, method); + + method = @event.InvokeMethod; + if (method != null) + AddSemantic (MethodSemanticsAttributes.Fire, @event, method); + + method = @event.RemoveMethod; + if (method != null) + AddSemantic (MethodSemanticsAttributes.RemoveOn, @event, method); + + if (@event.HasOtherMethods) + AddOtherSemantic (@event, @event.OtherMethods); + + if (@event.HasCustomAttributes) + AddCustomAttributes (@event); + } + + void AddSemantic (MethodSemanticsAttributes semantics, IMetadataTokenProvider provider, MethodDefinition method) + { + method.SemanticsAttributes = semantics; + var table = GetTable (Table.MethodSemantics); + + table.AddRow (new MethodSemanticsRow ( + semantics, + method.token.RID, + MakeCodedRID (provider, CodedIndex.HasSemantics))); + } + + void AddConstant (IConstantProvider owner, TypeReference type) + { + var constant = owner.Constant; + var etype = GetConstantType (type, constant); + + constant_table.AddRow (new ConstantRow ( + etype, + MakeCodedRID (owner.MetadataToken, CodedIndex.HasConstant), + GetBlobIndex (GetConstantSignature (etype, constant)))); + } + + static ElementType GetConstantType (TypeReference constant_type, object constant) + { + if (constant == null) + return ElementType.Class; + + var etype = constant_type.etype; + switch (etype) { + case ElementType.None: + var type = constant_type.CheckedResolve (); + if (type.IsEnum) + return GetConstantType (type.GetEnumUnderlyingType (), constant); + + return ElementType.Class; + case ElementType.String: + return ElementType.String; + case ElementType.Object: + return GetConstantType (constant.GetType ()); + case ElementType.Array: + case ElementType.SzArray: + case ElementType.MVar: + case ElementType.Var: + return ElementType.Class; + case ElementType.GenericInst: + var generic_instance = (GenericInstanceType)constant_type; + if (generic_instance.ElementType.IsTypeOf ("System", "Nullable`1")) + return GetConstantType (generic_instance.GenericArguments [0], constant); + + return GetConstantType (((TypeSpecification)constant_type).ElementType, constant); + case ElementType.CModOpt: + case ElementType.CModReqD: + case ElementType.ByRef: + case ElementType.Sentinel: + return GetConstantType (((TypeSpecification)constant_type).ElementType, constant); + case ElementType.Boolean: + case ElementType.Char: + case ElementType.I: + case ElementType.I1: + case ElementType.I2: + case ElementType.I4: + case ElementType.I8: + case ElementType.U: + case ElementType.U1: + case ElementType.U2: + case ElementType.U4: + case ElementType.U8: + case ElementType.R4: + case ElementType.R8: + return GetConstantType (constant.GetType ()); + default: + return etype; + } + } + + static ElementType GetConstantType (Type type) + { + switch (Type.GetTypeCode (type)) { + case TypeCode.Boolean: + return ElementType.Boolean; + case TypeCode.Byte: + return ElementType.U1; + case TypeCode.SByte: + return ElementType.I1; + case TypeCode.Char: + return ElementType.Char; + case TypeCode.Int16: + return ElementType.I2; + case TypeCode.UInt16: + return ElementType.U2; + case TypeCode.Int32: + return ElementType.I4; + case TypeCode.UInt32: + return ElementType.U4; + case TypeCode.Int64: + return ElementType.I8; + case TypeCode.UInt64: + return ElementType.U8; + case TypeCode.Single: + return ElementType.R4; + case TypeCode.Double: + return ElementType.R8; + case TypeCode.String: + return ElementType.String; + default: + throw new NotSupportedException (type.FullName); + } + } + + void AddCustomAttributes (ICustomAttributeProvider owner) + { + var custom_attributes = owner.CustomAttributes; + + for (int i = 0; i < custom_attributes.Count; i++) { + var attribute = custom_attributes [i]; + + var projection = WindowsRuntimeProjections.RemoveProjection (attribute); + + custom_attribute_table.AddRow (new CustomAttributeRow ( + MakeCodedRID (owner, CodedIndex.HasCustomAttribute), + MakeCodedRID (LookupToken (attribute.Constructor), CodedIndex.CustomAttributeType), + GetBlobIndex (GetCustomAttributeSignature (attribute)))); + + WindowsRuntimeProjections.ApplyProjection (attribute, projection); + } + } + + void AddSecurityDeclarations (ISecurityDeclarationProvider owner) + { + var declarations = owner.SecurityDeclarations; + + for (int i = 0; i < declarations.Count; i++) { + var declaration = declarations [i]; + + declsec_table.AddRow (new DeclSecurityRow ( + declaration.Action, + MakeCodedRID (owner, CodedIndex.HasDeclSecurity), + GetBlobIndex (GetSecurityDeclarationSignature (declaration)))); + } + } + + MetadataToken GetMemberRefToken (MemberReference member) + { + var row = CreateMemberRefRow (member); + + MetadataToken token; + if (!member_ref_map.TryGetValue (row, out token)) + token = AddMemberReference (member, row); + + return token; + } + + MemberRefRow CreateMemberRefRow (MemberReference member) + { + return new MemberRefRow ( + MakeCodedRID (GetTypeToken (member.DeclaringType), CodedIndex.MemberRefParent), + GetStringIndex (member.Name), + GetBlobIndex (GetMemberRefSignature (member))); + } + + MetadataToken AddMemberReference (MemberReference member, MemberRefRow row) + { + member.token = new MetadataToken (TokenType.MemberRef, member_ref_table.AddRow (row)); + + var token = member.token; + member_ref_map.Add (row, token); + return token; + } + + MetadataToken GetMethodSpecToken (MethodSpecification method_spec) + { + var row = CreateMethodSpecRow (method_spec); + + MetadataToken token; + if (method_spec_map.TryGetValue (row, out token)) + return token; + + AddMethodSpecification (method_spec, row); + + return method_spec.token; + } + + void AddMethodSpecification (MethodSpecification method_spec, MethodSpecRow row) + { + method_spec.token = new MetadataToken (TokenType.MethodSpec, method_spec_table.AddRow (row)); + method_spec_map.Add (row, method_spec.token); + } + + MethodSpecRow CreateMethodSpecRow (MethodSpecification method_spec) + { + return new MethodSpecRow ( + MakeCodedRID (LookupToken (method_spec.ElementMethod), CodedIndex.MethodDefOrRef), + GetBlobIndex (GetMethodSpecSignature (method_spec))); + } + + SignatureWriter CreateSignatureWriter () + { + return new SignatureWriter (this); + } + + SignatureWriter GetMethodSpecSignature (MethodSpecification method_spec) + { + if (!method_spec.IsGenericInstance) + throw new NotSupportedException (); + + var generic_instance = (GenericInstanceMethod)method_spec; + + var signature = CreateSignatureWriter (); + signature.WriteByte (0x0a); + + signature.WriteGenericInstanceSignature (generic_instance); + + return signature; + } + + public uint AddStandAloneSignature (uint signature) + { + return (uint)standalone_sig_table.AddRow (signature); + } + + public uint GetLocalVariableBlobIndex (Collection variables) + { + return GetBlobIndex (GetVariablesSignature (variables)); + } + + public uint GetCallSiteBlobIndex (CallSite call_site) + { + return GetBlobIndex (GetMethodSignature (call_site)); + } + + public uint GetConstantTypeBlobIndex (TypeReference constant_type) + { + return GetBlobIndex (GetConstantTypeSignature (constant_type)); + } + + SignatureWriter GetVariablesSignature (Collection variables) + { + var signature = CreateSignatureWriter (); + signature.WriteByte (0x7); + signature.WriteCompressedUInt32 ((uint)variables.Count); + for (int i = 0; i < variables.Count; i++) + signature.WriteTypeSignature (variables [i].VariableType); + return signature; + } + + SignatureWriter GetConstantTypeSignature (TypeReference constant_type) + { + var signature = CreateSignatureWriter (); + signature.WriteByte (0x6); + signature.WriteTypeSignature (constant_type); + return signature; + } + + SignatureWriter GetFieldSignature (FieldReference field) + { + var signature = CreateSignatureWriter (); + signature.WriteByte (0x6); + signature.WriteTypeSignature (field.FieldType); + return signature; + } + + SignatureWriter GetMethodSignature (IMethodSignature method) + { + var signature = CreateSignatureWriter (); + signature.WriteMethodSignature (method); + return signature; + } + + SignatureWriter GetMemberRefSignature (MemberReference member) + { + var field = member as FieldReference; + if (field != null) + return GetFieldSignature (field); + + var method = member as MethodReference; + if (method != null) + return GetMethodSignature (method); + + throw new NotSupportedException (); + } + + SignatureWriter GetPropertySignature (PropertyDefinition property) + { + var signature = CreateSignatureWriter (); + byte calling_convention = 0x8; + if (property.HasThis) + calling_convention |= 0x20; + + uint param_count = 0; + Collection parameters = null; + + if (property.HasParameters) { + parameters = property.Parameters; + param_count = (uint)parameters.Count; + } + + signature.WriteByte (calling_convention); + signature.WriteCompressedUInt32 (param_count); + signature.WriteTypeSignature (property.PropertyType); + + if (param_count == 0) + return signature; + + for (int i = 0; i < param_count; i++) + signature.WriteTypeSignature (parameters [i].ParameterType); + + return signature; + } + + SignatureWriter GetTypeSpecSignature (TypeReference type) + { + var signature = CreateSignatureWriter (); + signature.WriteTypeSignature (type); + return signature; + } + + SignatureWriter GetConstantSignature (ElementType type, object value) + { + var signature = CreateSignatureWriter (); + + switch (type) { + case ElementType.Array: + case ElementType.SzArray: + case ElementType.Class: + case ElementType.Object: + case ElementType.None: + case ElementType.Var: + case ElementType.MVar: + signature.WriteInt32 (0); + break; + case ElementType.String: + signature.WriteConstantString ((string)value); + break; + default: + signature.WriteConstantPrimitive (value); + break; + } + + return signature; + } + + SignatureWriter GetCustomAttributeSignature (CustomAttribute attribute) + { + var signature = CreateSignatureWriter (); + if (!attribute.resolved) { + signature.WriteBytes (attribute.GetBlob ()); + return signature; + } + + signature.WriteUInt16 (0x0001); + + signature.WriteCustomAttributeConstructorArguments (attribute); + + signature.WriteCustomAttributeNamedArguments (attribute); + + return signature; + } + + SignatureWriter GetSecurityDeclarationSignature (SecurityDeclaration declaration) + { + var signature = CreateSignatureWriter (); + + if (!declaration.resolved) + signature.WriteBytes (declaration.GetBlob ()); + else if (module.Runtime < TargetRuntime.Net_2_0) + signature.WriteXmlSecurityDeclaration (declaration); + else + signature.WriteSecurityDeclaration (declaration); + + return signature; + } + + SignatureWriter GetMarshalInfoSignature (IMarshalInfoProvider owner) + { + var signature = CreateSignatureWriter (); + + signature.WriteMarshalInfo (owner.MarshalInfo); + + return signature; + } + + static Exception CreateForeignMemberException (MemberReference member) + { + return new ArgumentException (string.Format ("Member '{0}' is declared in another module and needs to be imported", member)); + } + + public MetadataToken LookupToken (IMetadataTokenProvider provider) + { + if (provider == null) + throw new ArgumentNullException (); + + if (metadata_builder != null) + return metadata_builder.LookupToken (provider); + + var member = provider as MemberReference; + if (member == null || member.Module != module) + throw CreateForeignMemberException (member); + + var token = provider.MetadataToken; + + switch (token.TokenType) { + case TokenType.TypeDef: + case TokenType.Method: + case TokenType.Field: + case TokenType.Event: + case TokenType.Property: + return token; + case TokenType.TypeRef: + case TokenType.TypeSpec: + case TokenType.GenericParam: + return GetTypeToken ((TypeReference)provider); + case TokenType.MethodSpec: + return GetMethodSpecToken ((MethodSpecification)provider); + case TokenType.MemberRef: + return GetMemberRefToken (member); + default: + throw new NotSupportedException (); + } + } + + public void AddMethodDebugInformation (MethodDebugInformation method_info) + { + if (method_info.HasSequencePoints) + AddSequencePoints (method_info); + + if (method_info.Scope != null) + AddLocalScope (method_info, method_info.Scope); + + if (method_info.StateMachineKickOffMethod != null) + AddStateMachineMethod (method_info); + + AddCustomDebugInformations (method_info.Method); + } + + void AddStateMachineMethod (MethodDebugInformation method_info) + { + state_machine_method_table.AddRow (new StateMachineMethodRow (method_info.Method.MetadataToken.RID, method_info.StateMachineKickOffMethod.MetadataToken.RID)); + } + + void AddLocalScope (MethodDebugInformation method_info, ScopeDebugInformation scope) + { + var rid = local_scope_table.AddRow (new LocalScopeRow ( + method_info.Method.MetadataToken.RID, + scope.import != null ? AddImportScope (scope.import) : 0, + local_variable_rid, + local_constant_rid, + (uint)scope.Start.Offset, + (uint)((scope.End.IsEndOfMethod ? method_info.code_size : scope.End.Offset) - scope.Start.Offset))); + + scope.token = new MetadataToken (TokenType.LocalScope, rid); + + AddCustomDebugInformations (scope); + + if (scope.HasVariables) + AddLocalVariables (scope); + + if (scope.HasConstants) + AddLocalConstants (scope); + + for (int i = 0; i < scope.Scopes.Count; i++) + AddLocalScope (method_info, scope.Scopes [i]); + } + + void AddLocalVariables (ScopeDebugInformation scope) + { + for (int i = 0; i < scope.Variables.Count; i++) { + var variable = scope.Variables [i]; + local_variable_table.AddRow (new LocalVariableRow (variable.Attributes, (ushort)variable.Index, GetStringIndex (variable.Name))); + variable.token = new MetadataToken (TokenType.LocalVariable, local_variable_rid); + local_variable_rid++; + + AddCustomDebugInformations (variable); + } + } + + void AddLocalConstants (ScopeDebugInformation scope) + { + for (int i = 0; i < scope.Constants.Count; i++) { + var constant = scope.Constants [i]; + local_constant_table.AddRow (new LocalConstantRow (GetStringIndex (constant.Name), GetBlobIndex (GetConstantSignature (constant)))); + constant.token = new MetadataToken (TokenType.LocalConstant, local_constant_rid); + local_constant_rid++; + } + } + + SignatureWriter GetConstantSignature (ConstantDebugInformation constant) + { + var type = constant.ConstantType; + + var signature = CreateSignatureWriter (); + signature.WriteTypeSignature (type); + + if (type.IsTypeOf ("System", "Decimal")) { + var bits = decimal.GetBits ((decimal)constant.Value); + + var low = (uint)bits [0]; + var mid = (uint)bits [1]; + var high = (uint)bits [2]; + + var scale = (byte)(bits [3] >> 16); + var negative = (bits [3] & 0x80000000) != 0; + + signature.WriteByte ((byte)(scale | (negative ? 0x80 : 0x00))); + signature.WriteUInt32 (low); + signature.WriteUInt32 (mid); + signature.WriteUInt32 (high); + + return signature; + } + + if (type.IsTypeOf ("System", "DateTime")) { + var date = (DateTime)constant.Value; + signature.WriteInt64 (date.Ticks); + return signature; + } + + signature.WriteBytes (GetConstantSignature (type.etype, constant.Value)); + + return signature; + } + + public void AddCustomDebugInformations (ICustomDebugInformationProvider provider) + { + if (!provider.HasCustomDebugInformations) + return; + + var custom_infos = provider.CustomDebugInformations; + + for (int i = 0; i < custom_infos.Count; i++) { + var custom_info = custom_infos [i]; + switch (custom_info.Kind) { + case CustomDebugInformationKind.Binary: + var binary_info = (BinaryCustomDebugInformation)custom_info; + AddCustomDebugInformation (provider, binary_info, GetBlobIndex (binary_info.Data)); + break; + case CustomDebugInformationKind.AsyncMethodBody: + AddAsyncMethodBodyDebugInformation (provider, (AsyncMethodBodyDebugInformation)custom_info); + break; + case CustomDebugInformationKind.StateMachineScope: + AddStateMachineScopeDebugInformation (provider, (StateMachineScopeDebugInformation)custom_info); + break; + case CustomDebugInformationKind.EmbeddedSource: + AddEmbeddedSourceDebugInformation (provider, (EmbeddedSourceDebugInformation)custom_info); + break; + case CustomDebugInformationKind.SourceLink: + AddSourceLinkDebugInformation (provider, (SourceLinkDebugInformation)custom_info); + break; + default: + throw new NotImplementedException (); + } + } + } + + void AddStateMachineScopeDebugInformation (ICustomDebugInformationProvider provider, StateMachineScopeDebugInformation state_machine_scope) + { + var method_info = ((MethodDefinition)provider).DebugInformation; + + var signature = CreateSignatureWriter (); + + var scopes = state_machine_scope.Scopes; + + for (int i = 0; i < scopes.Count; i++) { + var scope = scopes [i]; + signature.WriteUInt32 ((uint)scope.Start.Offset); + + var end_offset = scope.End.IsEndOfMethod + ? method_info.code_size + : scope.End.Offset; + + signature.WriteUInt32 ((uint)(end_offset - scope.Start.Offset)); + } + + AddCustomDebugInformation (provider, state_machine_scope, signature); + } + + void AddAsyncMethodBodyDebugInformation (ICustomDebugInformationProvider provider, AsyncMethodBodyDebugInformation async_method) + { + var signature = CreateSignatureWriter (); + signature.WriteUInt32 ((uint)async_method.catch_handler.Offset + 1); + + if (!async_method.yields.IsNullOrEmpty ()) { + for (int i = 0; i < async_method.yields.Count; i++) { + signature.WriteUInt32 ((uint)async_method.yields [i].Offset); + signature.WriteUInt32 ((uint)async_method.resumes [i].Offset); + signature.WriteCompressedUInt32 (async_method.resume_methods [i].MetadataToken.RID); + } + } + + AddCustomDebugInformation (provider, async_method, signature); + } + + void AddEmbeddedSourceDebugInformation (ICustomDebugInformationProvider provider, EmbeddedSourceDebugInformation embedded_source) + { + var signature = CreateSignatureWriter (); + + if (!embedded_source.resolved) { + signature.WriteBytes (embedded_source.ReadRawEmbeddedSourceDebugInformation ()); + AddCustomDebugInformation (provider, embedded_source, signature); + return; + } + + var content = embedded_source.content ?? Empty.Array; + if (embedded_source.compress) { + signature.WriteInt32 (content.Length); + + var decompressed_stream = new MemoryStream (content); + var content_stream = new MemoryStream (); + + using (var compress_stream = new DeflateStream (content_stream, CompressionMode.Compress, leaveOpen: true)) + decompressed_stream.CopyTo (compress_stream); + + signature.WriteBytes (content_stream.ToArray ()); + } else { + signature.WriteInt32 (0); + signature.WriteBytes (content); + } + + AddCustomDebugInformation (provider, embedded_source, signature); + } + + void AddSourceLinkDebugInformation (ICustomDebugInformationProvider provider, SourceLinkDebugInformation source_link) + { + var signature = CreateSignatureWriter (); + signature.WriteBytes (Encoding.UTF8.GetBytes (source_link.content)); + + AddCustomDebugInformation (provider, source_link, signature); + } + + void AddCustomDebugInformation (ICustomDebugInformationProvider provider, CustomDebugInformation custom_info, SignatureWriter signature) + { + AddCustomDebugInformation (provider, custom_info, GetBlobIndex (signature)); + } + + void AddCustomDebugInformation (ICustomDebugInformationProvider provider, CustomDebugInformation custom_info, uint blob_index) + { + var rid = custom_debug_information_table.AddRow (new CustomDebugInformationRow ( + MakeCodedRID (provider.MetadataToken, CodedIndex.HasCustomDebugInformation), + GetGuidIndex (custom_info.Identifier), + blob_index)); + + custom_info.token = new MetadataToken (TokenType.CustomDebugInformation, rid); + } + + uint AddImportScope (ImportDebugInformation import) + { + uint parent = 0; + if (import.Parent != null) + parent = AddImportScope (import.Parent); + + uint targets_index = 0; + if (import.HasTargets) { + var signature = CreateSignatureWriter (); + + for (int i = 0; i < import.Targets.Count; i++) + AddImportTarget (import.Targets [i], signature); + + targets_index = GetBlobIndex (signature); + } + + var row = new ImportScopeRow (parent, targets_index); + + MetadataToken import_token; + if (import_scope_map.TryGetValue (row, out import_token)) + return import_token.RID; + + import_token = new MetadataToken (TokenType.ImportScope, import_scope_table.AddRow (row)); + import_scope_map.Add (row, import_token); + + return import_token.RID; + } + + void AddImportTarget (ImportTarget target, SignatureWriter signature) + { + signature.WriteCompressedUInt32 ((uint)target.kind); + + switch (target.kind) { + case ImportTargetKind.ImportNamespace: + signature.WriteCompressedUInt32 (GetUTF8StringBlobIndex (target.@namespace)); + break; + case ImportTargetKind.ImportNamespaceInAssembly: + signature.WriteCompressedUInt32 (target.reference.MetadataToken.RID); + signature.WriteCompressedUInt32 (GetUTF8StringBlobIndex (target.@namespace)); + break; + case ImportTargetKind.ImportType: + signature.WriteTypeToken (target.type); + break; + case ImportTargetKind.ImportXmlNamespaceWithAlias: + signature.WriteCompressedUInt32 (GetUTF8StringBlobIndex (target.alias)); + signature.WriteCompressedUInt32 (GetUTF8StringBlobIndex (target.@namespace)); + break; + case ImportTargetKind.ImportAlias: + signature.WriteCompressedUInt32 (GetUTF8StringBlobIndex (target.alias)); + break; + case ImportTargetKind.DefineAssemblyAlias: + signature.WriteCompressedUInt32 (GetUTF8StringBlobIndex (target.alias)); + signature.WriteCompressedUInt32 (target.reference.MetadataToken.RID); + break; + case ImportTargetKind.DefineNamespaceAlias: + signature.WriteCompressedUInt32 (GetUTF8StringBlobIndex (target.alias)); + signature.WriteCompressedUInt32 (GetUTF8StringBlobIndex (target.@namespace)); + break; + case ImportTargetKind.DefineNamespaceInAssemblyAlias: + signature.WriteCompressedUInt32 (GetUTF8StringBlobIndex (target.alias)); + signature.WriteCompressedUInt32 (target.reference.MetadataToken.RID); + signature.WriteCompressedUInt32 (GetUTF8StringBlobIndex (target.@namespace)); + break; + case ImportTargetKind.DefineTypeAlias: + signature.WriteCompressedUInt32 (GetUTF8StringBlobIndex (target.alias)); + signature.WriteTypeToken (target.type); + break; + } + } + + uint GetUTF8StringBlobIndex (string s) + { + return GetBlobIndex (Encoding.UTF8.GetBytes (s)); + } + + public MetadataToken GetDocumentToken (Document document) + { + MetadataToken token; + if (document_map.TryGetValue (document.Url, out token)) + return token; + + token = new MetadataToken (TokenType.Document, document_table.AddRow ( + new DocumentRow (GetBlobIndex (GetDocumentNameSignature (document)), + GetGuidIndex (document.HashAlgorithm.ToGuid ()), + GetBlobIndex (document.Hash), + GetGuidIndex (document.Language.ToGuid ())))); + + document.token = token; + + AddCustomDebugInformations (document); + + document_map.Add (document.Url, token); + + return token; + } + + SignatureWriter GetDocumentNameSignature (Document document) + { + var name = document.Url; + var signature = CreateSignatureWriter (); + + char separator; + if (!TryGetDocumentNameSeparator (name, out separator)) { + signature.WriteByte (0); + signature.WriteCompressedUInt32 (GetUTF8StringBlobIndex (name)); + return signature; + } + + signature.WriteByte ((byte)separator); + var parts = name.Split (new [] { separator }); + for (int i = 0; i < parts.Length; i++) { + if (parts [i] == String.Empty) + signature.WriteCompressedUInt32 (0); + else + signature.WriteCompressedUInt32 (GetUTF8StringBlobIndex (parts [i])); + } + + return signature; + } + + static bool TryGetDocumentNameSeparator (string path, out char separator) + { + const char unix = '/'; + const char win = '\\'; + const char zero = (char)0; + + separator = zero; + if (string.IsNullOrEmpty (path)) + return false; + + int unix_count = 0; + int win_count = 0; + + for (int i = 0; i < path.Length; i++) { + if (path [i] == unix) + unix_count++; + else if (path [i] == win) + win_count++; + } + + if (unix_count == 0 && win_count == 0) + return false; + + if (unix_count >= win_count) { + separator = unix; + return true; + } + + separator = win; + return true; + } + + void AddSequencePoints (MethodDebugInformation info) + { + var rid = info.Method.MetadataToken.RID; + + Document document; + if (info.TryGetUniqueDocument (out document)) + method_debug_information_table.rows [rid - 1].Col1 = GetDocumentToken (document).RID; + + var signature = CreateSignatureWriter (); + signature.WriteSequencePoints (info); + + method_debug_information_table.rows [rid - 1].Col2 = GetBlobIndex (signature); + } + + public void ComputeDeterministicMvid () + { + var guid = CryptoService.ComputeGuid (CryptoService.ComputeHash ( + data, + resources, + string_heap, + user_string_heap, + blob_heap, + table_heap, + code)); + + var position = guid_heap.position; + guid_heap.position = 0; + guid_heap.WriteBytes (guid.ToByteArray ()); + guid_heap.position = position; + + module.Mvid = guid; + } + } + + sealed class SignatureWriter : ByteBuffer { + + readonly MetadataBuilder metadata; + + public SignatureWriter (MetadataBuilder metadata) + : base (6) + { + this.metadata = metadata; + } + + public void WriteElementType (ElementType element_type) + { + WriteByte ((byte)element_type); + } + + public void WriteUTF8String (string @string) + { + if (@string == null) { + WriteByte (0xff); + return; + } + + var bytes = Encoding.UTF8.GetBytes (@string); + WriteCompressedUInt32 ((uint)bytes.Length); + WriteBytes (bytes); + } + + public void WriteMethodSignature (IMethodSignature method) + { + byte calling_convention = (byte)method.CallingConvention; + if (method.HasThis) + calling_convention |= 0x20; + if (method.ExplicitThis) + calling_convention |= 0x40; + + var generic_provider = method as IGenericParameterProvider; + var generic_arity = generic_provider != null && generic_provider.HasGenericParameters + ? generic_provider.GenericParameters.Count + : 0; + + if (generic_arity > 0) + calling_convention |= 0x10; + + var param_count = method.HasParameters ? method.Parameters.Count : 0; + + WriteByte (calling_convention); + + if (generic_arity > 0) + WriteCompressedUInt32 ((uint)generic_arity); + + WriteCompressedUInt32 ((uint)param_count); + WriteTypeSignature (method.ReturnType); + + if (param_count == 0) + return; + + var parameters = method.Parameters; + + for (int i = 0; i < param_count; i++) + WriteTypeSignature (parameters [i].ParameterType); + } + + uint MakeTypeDefOrRefCodedRID (TypeReference type) + { + return CodedIndex.TypeDefOrRef.CompressMetadataToken (metadata.LookupToken (type)); + } + + public void WriteTypeToken (TypeReference type) + { + WriteCompressedUInt32 (MakeTypeDefOrRefCodedRID (type)); + } + + public void WriteTypeSignature (TypeReference type) + { + if (type == null) + throw new ArgumentNullException (); + + var etype = type.etype; + + switch (etype) { + case ElementType.MVar: + case ElementType.Var: { + var generic_parameter = (GenericParameter)type; + + WriteElementType (etype); + var position = generic_parameter.Position; + if (position == -1) + throw new NotSupportedException (); + + WriteCompressedUInt32 ((uint)position); + break; + } + + case ElementType.GenericInst: { + var generic_instance = (GenericInstanceType)type; + WriteElementType (ElementType.GenericInst); + WriteElementType (generic_instance.IsValueType ? ElementType.ValueType : ElementType.Class); + WriteCompressedUInt32 (MakeTypeDefOrRefCodedRID (generic_instance.ElementType)); + + WriteGenericInstanceSignature (generic_instance); + break; + } + + case ElementType.Ptr: + case ElementType.ByRef: + case ElementType.Pinned: + case ElementType.Sentinel: { + var type_spec = (TypeSpecification)type; + WriteElementType (etype); + WriteTypeSignature (type_spec.ElementType); + break; + } + + case ElementType.FnPtr: { + var fptr = (FunctionPointerType)type; + WriteElementType (ElementType.FnPtr); + WriteMethodSignature (fptr); + break; + } + + case ElementType.CModOpt: + case ElementType.CModReqD: { + var modifier = (IModifierType)type; + WriteModifierSignature (etype, modifier); + break; + } + + case ElementType.Array: { + var array = (ArrayType)type; + if (!array.IsVector) { + WriteArrayTypeSignature (array); + break; + } + + WriteElementType (ElementType.SzArray); + WriteTypeSignature (array.ElementType); + break; + } + + case ElementType.None: { + WriteElementType (type.IsValueType ? ElementType.ValueType : ElementType.Class); + WriteCompressedUInt32 (MakeTypeDefOrRefCodedRID (type)); + break; + } + + default: + if (!TryWriteElementType (type)) + throw new NotSupportedException (); + + break; + + } + } + + void WriteArrayTypeSignature (ArrayType array) + { + WriteElementType (ElementType.Array); + WriteTypeSignature (array.ElementType); + + var dimensions = array.Dimensions; + var rank = dimensions.Count; + + WriteCompressedUInt32 ((uint)rank); + + var sized = 0; + var lbounds = 0; + + for (int i = 0; i < rank; i++) { + var dimension = dimensions [i]; + + if (dimension.UpperBound.HasValue) { + sized++; + lbounds++; + } else if (dimension.LowerBound.HasValue) + lbounds++; + } + + var sizes = new int [sized]; + var low_bounds = new int [lbounds]; + + for (int i = 0; i < lbounds; i++) { + var dimension = dimensions [i]; + low_bounds [i] = dimension.LowerBound.GetValueOrDefault (); + if (dimension.UpperBound.HasValue) + sizes [i] = dimension.UpperBound.Value - low_bounds [i] + 1; + } + + WriteCompressedUInt32 ((uint)sized); + for (int i = 0; i < sized; i++) + WriteCompressedUInt32 ((uint)sizes [i]); + + WriteCompressedUInt32 ((uint)lbounds); + for (int i = 0; i < lbounds; i++) + WriteCompressedInt32 (low_bounds [i]); + } + + public void WriteGenericInstanceSignature (IGenericInstance instance) + { + var generic_arguments = instance.GenericArguments; + var arity = generic_arguments.Count; + + WriteCompressedUInt32 ((uint)arity); + for (int i = 0; i < arity; i++) + WriteTypeSignature (generic_arguments [i]); + } + + void WriteModifierSignature (ElementType element_type, IModifierType type) + { + WriteElementType (element_type); + WriteCompressedUInt32 (MakeTypeDefOrRefCodedRID (type.ModifierType)); + WriteTypeSignature (type.ElementType); + } + + bool TryWriteElementType (TypeReference type) + { + var element = type.etype; + + if (element == ElementType.None) + return false; + + WriteElementType (element); + return true; + } + + public void WriteConstantString (string value) + { + if (value != null) + WriteBytes (Encoding.Unicode.GetBytes (value)); + else + WriteByte (0xff); + } + + public void WriteConstantPrimitive (object value) + { + WritePrimitiveValue (value); + } + + public void WriteCustomAttributeConstructorArguments (CustomAttribute attribute) + { + if (!attribute.HasConstructorArguments) + return; + + var arguments = attribute.ConstructorArguments; + var parameters = attribute.Constructor.Parameters; + + if (parameters.Count != arguments.Count) + throw new InvalidOperationException (); + + for (int i = 0; i < arguments.Count; i++) + WriteCustomAttributeFixedArgument (parameters [i].ParameterType, arguments [i]); + } + + void WriteCustomAttributeFixedArgument (TypeReference type, CustomAttributeArgument argument) + { + if (type.IsArray) { + WriteCustomAttributeFixedArrayArgument ((ArrayType)type, argument); + return; + } + + WriteCustomAttributeElement (type, argument); + } + + void WriteCustomAttributeFixedArrayArgument (ArrayType type, CustomAttributeArgument argument) + { + var values = argument.Value as CustomAttributeArgument []; + + if (values == null) { + WriteUInt32 (0xffffffff); + return; + } + + WriteInt32 (values.Length); + + if (values.Length == 0) + return; + + var element_type = type.ElementType; + + for (int i = 0; i < values.Length; i++) + WriteCustomAttributeElement (element_type, values [i]); + } + + void WriteCustomAttributeElement (TypeReference type, CustomAttributeArgument argument) + { + if (type.IsArray) { + WriteCustomAttributeFixedArrayArgument ((ArrayType)type, argument); + return; + } + + if (type.etype == ElementType.Object) { + argument = (CustomAttributeArgument)argument.Value; + type = argument.Type; + + WriteCustomAttributeFieldOrPropType (type); + WriteCustomAttributeElement (type, argument); + return; + } + + WriteCustomAttributeValue (type, argument.Value); + } + + void WriteCustomAttributeValue (TypeReference type, object value) + { + var etype = type.etype; + + switch (etype) { + case ElementType.String: + var @string = (string)value; + if (@string == null) + WriteByte (0xff); + else + WriteUTF8String (@string); + break; + case ElementType.None: + if (type.IsTypeOf ("System", "Type")) + WriteCustomAttributeTypeValue ((TypeReference)value); + else + WriteCustomAttributeEnumValue (type, value); + break; + default: + WritePrimitiveValue (value); + break; + } + } + + private void WriteCustomAttributeTypeValue (TypeReference value) + { + var typeDefinition = value as TypeDefinition; + + if (typeDefinition != null) { + TypeDefinition outermostDeclaringType = typeDefinition; + while (outermostDeclaringType.DeclaringType != null) + outermostDeclaringType = outermostDeclaringType.DeclaringType; + + // In CLR .winmd files, custom attribute arguments reference unmangled type names (rather than Name) + if (WindowsRuntimeProjections.IsClrImplementationType (outermostDeclaringType)) { + WindowsRuntimeProjections.Project (outermostDeclaringType); + WriteTypeReference (value); + WindowsRuntimeProjections.RemoveProjection (outermostDeclaringType); + return; + } + } + + WriteTypeReference (value); + } + + void WritePrimitiveValue (object value) + { + if (value == null) + throw new ArgumentNullException (); + + switch (Type.GetTypeCode (value.GetType ())) { + case TypeCode.Boolean: + WriteByte ((byte)(((bool)value) ? 1 : 0)); + break; + case TypeCode.Byte: + WriteByte ((byte)value); + break; + case TypeCode.SByte: + WriteSByte ((sbyte)value); + break; + case TypeCode.Int16: + WriteInt16 ((short)value); + break; + case TypeCode.UInt16: + WriteUInt16 ((ushort)value); + break; + case TypeCode.Char: + WriteInt16 ((short)(char)value); + break; + case TypeCode.Int32: + WriteInt32 ((int)value); + break; + case TypeCode.UInt32: + WriteUInt32 ((uint)value); + break; + case TypeCode.Single: + WriteSingle ((float)value); + break; + case TypeCode.Int64: + WriteInt64 ((long)value); + break; + case TypeCode.UInt64: + WriteUInt64 ((ulong)value); + break; + case TypeCode.Double: + WriteDouble ((double)value); + break; + default: + throw new NotSupportedException (value.GetType ().FullName); + } + } + + void WriteCustomAttributeEnumValue (TypeReference enum_type, object value) + { + var type = enum_type.CheckedResolve (); + if (!type.IsEnum) + throw new ArgumentException (); + + WriteCustomAttributeValue (type.GetEnumUnderlyingType (), value); + } + + void WriteCustomAttributeFieldOrPropType (TypeReference type) + { + if (type.IsArray) { + var array = (ArrayType)type; + WriteElementType (ElementType.SzArray); + WriteCustomAttributeFieldOrPropType (array.ElementType); + return; + } + + var etype = type.etype; + + switch (etype) { + case ElementType.Object: + WriteElementType (ElementType.Boxed); + return; + case ElementType.None: + if (type.IsTypeOf ("System", "Type")) + WriteElementType (ElementType.Type); + else { + WriteElementType (ElementType.Enum); + WriteTypeReference (type); + } + return; + default: + WriteElementType (etype); + return; + } + } + + public void WriteCustomAttributeNamedArguments (CustomAttribute attribute) + { + var count = GetNamedArgumentCount (attribute); + + WriteUInt16 ((ushort)count); + + if (count == 0) + return; + + WriteICustomAttributeNamedArguments (attribute); + } + + static int GetNamedArgumentCount (ICustomAttribute attribute) + { + int count = 0; + + if (attribute.HasFields) + count += attribute.Fields.Count; + + if (attribute.HasProperties) + count += attribute.Properties.Count; + + return count; + } + + void WriteICustomAttributeNamedArguments (ICustomAttribute attribute) + { + if (attribute.HasFields) + WriteCustomAttributeNamedArguments (0x53, attribute.Fields); + + if (attribute.HasProperties) + WriteCustomAttributeNamedArguments (0x54, attribute.Properties); + } + + void WriteCustomAttributeNamedArguments (byte kind, Collection named_arguments) + { + for (int i = 0; i < named_arguments.Count; i++) + WriteCustomAttributeNamedArgument (kind, named_arguments [i]); + } + + void WriteCustomAttributeNamedArgument (byte kind, CustomAttributeNamedArgument named_argument) + { + var argument = named_argument.Argument; + + WriteByte (kind); + WriteCustomAttributeFieldOrPropType (argument.Type); + WriteUTF8String (named_argument.Name); + WriteCustomAttributeFixedArgument (argument.Type, argument); + } + + void WriteSecurityAttribute (SecurityAttribute attribute) + { + WriteTypeReference (attribute.AttributeType); + + var count = GetNamedArgumentCount (attribute); + + if (count == 0) { + WriteCompressedUInt32 (1); // length + WriteCompressedUInt32 (0); // count + return; + } + + var buffer = new SignatureWriter (metadata); + buffer.WriteCompressedUInt32 ((uint)count); + buffer.WriteICustomAttributeNamedArguments (attribute); + + WriteCompressedUInt32 ((uint)buffer.length); + WriteBytes (buffer); + } + + public void WriteSecurityDeclaration (SecurityDeclaration declaration) + { + WriteByte ((byte)'.'); + + var attributes = declaration.security_attributes; + if (attributes == null) + throw new NotSupportedException (); + + WriteCompressedUInt32 ((uint)attributes.Count); + + for (int i = 0; i < attributes.Count; i++) + WriteSecurityAttribute (attributes [i]); + } + + public void WriteXmlSecurityDeclaration (SecurityDeclaration declaration) + { + var xml = GetXmlSecurityDeclaration (declaration); + if (xml == null) + throw new NotSupportedException (); + + WriteBytes (Encoding.Unicode.GetBytes (xml)); + } + + static string GetXmlSecurityDeclaration (SecurityDeclaration declaration) + { + if (declaration.security_attributes == null || declaration.security_attributes.Count != 1) + return null; + + var attribute = declaration.security_attributes [0]; + + if (!attribute.AttributeType.IsTypeOf ("System.Security.Permissions", "PermissionSetAttribute")) + return null; + + if (attribute.properties == null || attribute.properties.Count != 1) + return null; + + var property = attribute.properties [0]; + if (property.Name != "XML") + return null; + + return (string)property.Argument.Value; + } + + void WriteTypeReference (TypeReference type) + { + WriteUTF8String (TypeParser.ToParseable (type, top_level: false)); + } + + public void WriteMarshalInfo (MarshalInfo marshal_info) + { + WriteNativeType (marshal_info.native); + + switch (marshal_info.native) { + case NativeType.Array: { + var array = (ArrayMarshalInfo)marshal_info; + if (array.element_type != NativeType.None) + WriteNativeType (array.element_type); + if (array.size_parameter_index > -1) + WriteCompressedUInt32 ((uint)array.size_parameter_index); + if (array.size > -1) + WriteCompressedUInt32 ((uint)array.size); + if (array.size_parameter_multiplier > -1) + WriteCompressedUInt32 ((uint)array.size_parameter_multiplier); + return; + } + case NativeType.SafeArray: { + var array = (SafeArrayMarshalInfo)marshal_info; + if (array.element_type != VariantType.None) + WriteVariantType (array.element_type); + return; + } + case NativeType.FixedArray: { + var array = (FixedArrayMarshalInfo)marshal_info; + if (array.size > -1) + WriteCompressedUInt32 ((uint)array.size); + if (array.element_type != NativeType.None) + WriteNativeType (array.element_type); + return; + } + case NativeType.FixedSysString: + var sys_string = (FixedSysStringMarshalInfo)marshal_info; + if (sys_string.size > -1) + WriteCompressedUInt32 ((uint)sys_string.size); + return; + case NativeType.CustomMarshaler: + var marshaler = (CustomMarshalInfo)marshal_info; + WriteUTF8String (marshaler.guid != Guid.Empty ? marshaler.guid.ToString () : string.Empty); + WriteUTF8String (marshaler.unmanaged_type); + WriteTypeReference (marshaler.managed_type); + WriteUTF8String (marshaler.cookie); + return; + } + } + + void WriteNativeType (NativeType native) + { + WriteByte ((byte)native); + } + + void WriteVariantType (VariantType variant) + { + WriteByte ((byte)variant); + } + + public void WriteSequencePoints (MethodDebugInformation info) + { + var start_line = -1; + var start_column = -1; + + WriteCompressedUInt32 (info.local_var_token.RID); + + Document previous_document; + if (!info.TryGetUniqueDocument (out previous_document)) + previous_document = null; + + for (int i = 0; i < info.SequencePoints.Count; i++) { + var sequence_point = info.SequencePoints [i]; + + var document = sequence_point.Document; + if (previous_document != document) { + var document_token = metadata.GetDocumentToken (document); + + if (previous_document != null) + WriteCompressedUInt32 (0); + + WriteCompressedUInt32 (document_token.RID); + previous_document = document; + } + + if (i > 0) + WriteCompressedUInt32 ((uint)(sequence_point.Offset - info.SequencePoints [i - 1].Offset)); + else + WriteCompressedUInt32 ((uint)sequence_point.Offset); + + if (sequence_point.IsHidden) { + WriteInt16 (0); + continue; + } + + var delta_lines = sequence_point.EndLine - sequence_point.StartLine; + var delta_columns = sequence_point.EndColumn - sequence_point.StartColumn; + + WriteCompressedUInt32 ((uint)delta_lines); + + if (delta_lines == 0) + WriteCompressedUInt32 ((uint)delta_columns); + else + WriteCompressedInt32 (delta_columns); + + if (start_line < 0) { + WriteCompressedUInt32 ((uint)sequence_point.StartLine); + WriteCompressedUInt32 ((uint)sequence_point.StartColumn); + } else { + WriteCompressedInt32 (sequence_point.StartLine - start_line); + WriteCompressedInt32 (sequence_point.StartColumn - start_column); + } + + start_line = sequence_point.StartLine; + start_column = sequence_point.StartColumn; + } + } + } + + static partial class Mixin { + + public static bool TryGetUniqueDocument (this MethodDebugInformation info, out Document document) + { + document = info.SequencePoints [0].Document; + + for (int i = 1; i < info.SequencePoints.Count; i++) { + var sequence_point = info.SequencePoints [i]; + if (sequence_point.Document != document) + return false; + } + + return true; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyWriter.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyWriter.cs.meta new file mode 100644 index 0000000..a25718a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyWriter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9a88ce645df13da4aa9eca43f013f6ae +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/BaseAssemblyResolver.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/BaseAssemblyResolver.cs new file mode 100644 index 0000000..cb3c16a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/BaseAssemblyResolver.cs @@ -0,0 +1,406 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace MonoFN.Cecil { + + public delegate AssemblyDefinition AssemblyResolveEventHandler (object sender, AssemblyNameReference reference); + + public sealed class AssemblyResolveEventArgs : EventArgs { + + readonly AssemblyNameReference reference; + + public AssemblyNameReference AssemblyReference { + get { return reference; } + } + + public AssemblyResolveEventArgs (AssemblyNameReference reference) + { + this.reference = reference; + } + } + +#if !NET_CORE + [Serializable] +#endif + public sealed class AssemblyResolutionException : FileNotFoundException { + + readonly AssemblyNameReference reference; + + public AssemblyNameReference AssemblyReference { + get { return reference; } + } + + public AssemblyResolutionException (AssemblyNameReference reference) + : this (reference, null) + { + } + + public AssemblyResolutionException (AssemblyNameReference reference, Exception innerException) + : base (string.Format ("Failed to resolve assembly: '{0}'", reference), innerException) + { + this.reference = reference; + } + +#if !NET_CORE + AssemblyResolutionException ( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) + : base (info, context) + { + } +#endif + } + + public abstract class BaseAssemblyResolver : IAssemblyResolver { + + static readonly bool on_mono = Type.GetType ("MonoFN.Runtime") != null; + + readonly Collection directories; + +#if NET_CORE + // Maps file names of available trusted platform assemblies to their full paths. + // Internal for testing. + internal static readonly Lazy> TrustedPlatformAssemblies = new Lazy> (CreateTrustedPlatformAssemblyMap); +#else + Collection gac_paths; +#endif + + public void AddSearchDirectory (string directory) + { + directories.Add (directory); + } + + public void RemoveSearchDirectory (string directory) + { + directories.Remove (directory); + } + + public string [] GetSearchDirectories () + { + var directories = new string [this.directories.size]; + Array.Copy (this.directories.items, directories, directories.Length); + return directories; + } + + public event AssemblyResolveEventHandler ResolveFailure; + + protected BaseAssemblyResolver () + { + directories = new Collection (2) { ".", "bin" }; + } + + AssemblyDefinition GetAssembly (string file, ReaderParameters parameters) + { + if (parameters.AssemblyResolver == null) + parameters.AssemblyResolver = this; + + return ModuleDefinition.ReadModule (file, parameters).Assembly; + } + + public virtual AssemblyDefinition Resolve (AssemblyNameReference name) + { + return Resolve (name, new ReaderParameters ()); + } + + public virtual AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters) + { + Mixin.CheckName (name); + Mixin.CheckParameters (parameters); + + var assembly = SearchDirectory (name, directories, parameters); + if (assembly != null) + return assembly; + + if (name.IsRetargetable) { + // if the reference is retargetable, zero it + name = new AssemblyNameReference (name.Name, Mixin.ZeroVersion) { + PublicKeyToken = Empty.Array, + }; + } + +#if NET_CORE + assembly = SearchTrustedPlatformAssemblies (name, parameters); + if (assembly != null) + return assembly; +#else + var framework_dir = Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName); + var framework_dirs = on_mono + ? new [] { framework_dir, Path.Combine (framework_dir, "Facades") } + : new [] { framework_dir }; + + if (IsZero (name.Version)) { + assembly = SearchDirectory (name, framework_dirs, parameters); + if (assembly != null) + return assembly; + } + + if (name.Name == "mscorlib") { + assembly = GetCorlib (name, parameters); + if (assembly != null) + return assembly; + } + + assembly = GetAssemblyInGac (name, parameters); + if (assembly != null) + return assembly; + + assembly = SearchDirectory (name, framework_dirs, parameters); + if (assembly != null) + return assembly; +#endif + if (ResolveFailure != null) { + assembly = ResolveFailure (this, name); + if (assembly != null) + return assembly; + } + + throw new AssemblyResolutionException (name); + } + +#if NET_CORE + AssemblyDefinition SearchTrustedPlatformAssemblies (AssemblyNameReference name, ReaderParameters parameters) + { + if (name.IsWindowsRuntime) + return null; + + if (TrustedPlatformAssemblies.Value.TryGetValue (name.Name, out string path)) + return GetAssembly (path, parameters); + + return null; + } + + static Dictionary CreateTrustedPlatformAssemblyMap () + { + var result = new Dictionary (StringComparer.OrdinalIgnoreCase); + + string paths; + + try { + paths = (string) AppDomain.CurrentDomain.GetData ("TRUSTED_PLATFORM_ASSEMBLIES"); + } catch { + paths = null; + } + + if (paths == null) + return result; + + foreach (var path in paths.Split (Path.PathSeparator)) + if (string.Equals (Path.GetExtension (path), ".dll", StringComparison.OrdinalIgnoreCase)) + result [Path.GetFileNameWithoutExtension (path)] = path; + + return result; + } +#endif + + protected virtual AssemblyDefinition SearchDirectory (AssemblyNameReference name, IEnumerable directories, ReaderParameters parameters) + { + var extensions = name.IsWindowsRuntime ? new [] { ".winmd", ".dll" } : new [] { ".exe", ".dll" }; + foreach (var directory in directories) { + foreach (var extension in extensions) { + string file = Path.Combine (directory, name.Name + extension); + if (!File.Exists (file)) + continue; + try { + return GetAssembly (file, parameters); + } + catch (System.BadImageFormatException) { + continue; + } + } + } + + return null; + } + + static bool IsZero (Version version) + { + return version.Major == 0 && version.Minor == 0 && version.Build == 0 && version.Revision == 0; + } + +#if !NET_CORE + AssemblyDefinition GetCorlib (AssemblyNameReference reference, ReaderParameters parameters) + { + var version = reference.Version; + var corlib = typeof (object).Assembly.GetName (); + if (corlib.Version == version || IsZero (version)) + return GetAssembly (typeof (object).Module.FullyQualifiedName, parameters); + + var path = Directory.GetParent ( + Directory.GetParent ( + typeof (object).Module.FullyQualifiedName).FullName + ).FullName; + + if (on_mono) { + if (version.Major == 1) + path = Path.Combine (path, "1.0"); + else if (version.Major == 2) { + if (version.MajorRevision == 5) + path = Path.Combine (path, "2.1"); + else + path = Path.Combine (path, "2.0"); + } else if (version.Major == 4) + path = Path.Combine (path, "4.0"); + else + throw new NotSupportedException ("Version not supported: " + version); + } else { + switch (version.Major) { + case 1: + if (version.MajorRevision == 3300) + path = Path.Combine (path, "v1.0.3705"); + else + path = Path.Combine (path, "v1.1.4322"); + break; + case 2: + path = Path.Combine (path, "v2.0.50727"); + break; + case 4: + path = Path.Combine (path, "v4.0.30319"); + break; + default: + throw new NotSupportedException ("Version not supported: " + version); + } + } + + var file = Path.Combine (path, "mscorlib.dll"); + if (File.Exists (file)) + return GetAssembly (file, parameters); + + if (on_mono && Directory.Exists (path + "-api")) { + file = Path.Combine (path + "-api", "mscorlib.dll"); + if (File.Exists (file)) + return GetAssembly (file, parameters); + } + + return null; + } + + static Collection GetGacPaths () + { + if (on_mono) + return GetDefaultMonoGacPaths (); + + var paths = new Collection (2); + var windir = Environment.GetEnvironmentVariable ("WINDIR"); + if (windir == null) + return paths; + + paths.Add (Path.Combine (windir, "assembly")); + paths.Add (Path.Combine (windir, Path.Combine ("Microsoft.NET", "assembly"))); + return paths; + } + + static Collection GetDefaultMonoGacPaths () + { + var paths = new Collection (1); + var gac = GetCurrentMonoGac (); + if (gac != null) + paths.Add (gac); + + var gac_paths_env = Environment.GetEnvironmentVariable ("MONO_GAC_PREFIX"); + if (string.IsNullOrEmpty (gac_paths_env)) + return paths; + + var prefixes = gac_paths_env.Split (Path.PathSeparator); + foreach (var prefix in prefixes) { + if (string.IsNullOrEmpty (prefix)) + continue; + + var gac_path = Path.Combine (Path.Combine (Path.Combine (prefix, "lib"), "mono"), "gac"); + if (Directory.Exists (gac_path) && !paths.Contains (gac)) + paths.Add (gac_path); + } + + return paths; + } + + static string GetCurrentMonoGac () + { + return Path.Combine ( + Directory.GetParent ( + Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName)).FullName, + "gac"); + } + + AssemblyDefinition GetAssemblyInGac (AssemblyNameReference reference, ReaderParameters parameters) + { + if (reference.PublicKeyToken == null || reference.PublicKeyToken.Length == 0) + return null; + + if (gac_paths == null) + gac_paths = GetGacPaths (); + + if (on_mono) + return GetAssemblyInMonoGac (reference, parameters); + + return GetAssemblyInNetGac (reference, parameters); + } + + AssemblyDefinition GetAssemblyInMonoGac (AssemblyNameReference reference, ReaderParameters parameters) + { + for (int i = 0; i < gac_paths.Count; i++) { + var gac_path = gac_paths [i]; + var file = GetAssemblyFile (reference, string.Empty, gac_path); + if (File.Exists (file)) + return GetAssembly (file, parameters); + } + + return null; + } + + AssemblyDefinition GetAssemblyInNetGac (AssemblyNameReference reference, ReaderParameters parameters) + { + var gacs = new [] { "GAC_MSIL", "GAC_32", "GAC_64", "GAC" }; + var prefixes = new [] { string.Empty, "v4.0_" }; + + for (int i = 0; i < gac_paths.Count; i++) { + for (int j = 0; j < gacs.Length; j++) { + var gac = Path.Combine (gac_paths [i], gacs [j]); + var file = GetAssemblyFile (reference, prefixes [i], gac); + if (Directory.Exists (gac) && File.Exists (file)) + return GetAssembly (file, parameters); + } + } + + return null; + } + + static string GetAssemblyFile (AssemblyNameReference reference, string prefix, string gac) + { + var gac_folder = new StringBuilder () + .Append (prefix) + .Append (reference.Version) + .Append ("__"); + + for (int i = 0; i < reference.PublicKeyToken.Length; i++) + gac_folder.Append (reference.PublicKeyToken [i].ToString ("x2")); + + return Path.Combine ( + Path.Combine ( + Path.Combine (gac, reference.Name), gac_folder.ToString ()), + reference.Name + ".dll"); + } +#endif + public void Dispose () + { + Dispose (true); + GC.SuppressFinalize (this); + } + + protected virtual void Dispose (bool disposing) + { + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/BaseAssemblyResolver.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/BaseAssemblyResolver.cs.meta new file mode 100644 index 0000000..87211a6 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/BaseAssemblyResolver.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4ab1d53794ef7444c81e276b5a3a5c2b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/CallSite.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/CallSite.cs new file mode 100644 index 0000000..34e8ac7 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/CallSite.cs @@ -0,0 +1,105 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System; +using System.Text; + +namespace MonoFN.Cecil { + + public sealed class CallSite : IMethodSignature { + + readonly MethodReference signature; + + public bool HasThis { + get { return signature.HasThis; } + set { signature.HasThis = value; } + } + + public bool ExplicitThis { + get { return signature.ExplicitThis; } + set { signature.ExplicitThis = value; } + } + + public MethodCallingConvention CallingConvention { + get { return signature.CallingConvention; } + set { signature.CallingConvention = value; } + } + + public bool HasParameters { + get { return signature.HasParameters; } + } + + public Collection Parameters { + get { return signature.Parameters; } + } + + public TypeReference ReturnType { + get { return signature.MethodReturnType.ReturnType; } + set { signature.MethodReturnType.ReturnType = value; } + } + + public MethodReturnType MethodReturnType { + get { return signature.MethodReturnType; } + } + + public string Name { + get { return string.Empty; } + set { throw new InvalidOperationException (); } + } + + public string Namespace { + get { return string.Empty; } + set { throw new InvalidOperationException (); } + } + + public ModuleDefinition Module { + get { return ReturnType.Module; } + } + + public IMetadataScope Scope { + get { return signature.ReturnType.Scope; } + } + + public MetadataToken MetadataToken { + get { return signature.token; } + set { signature.token = value; } + } + + public string FullName { + get { + var signature = new StringBuilder (); + signature.Append (ReturnType.FullName); + this.MethodSignatureFullName (signature); + return signature.ToString (); + } + } + + internal CallSite () + { + this.signature = new MethodReference (); + this.signature.token = new MetadataToken (TokenType.Signature, 0); + } + + public CallSite (TypeReference returnType) + : this () + { + if (returnType == null) + throw new ArgumentNullException ("returnType"); + + this.signature.ReturnType = returnType; + } + + public override string ToString () + { + return FullName; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/CallSite.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/CallSite.cs.meta new file mode 100644 index 0000000..8eac3ae --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/CallSite.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 087823dce9623d348927f193f95a1807 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Consts.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Consts.cs new file mode 100644 index 0000000..54f90e6 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Consts.cs @@ -0,0 +1,4 @@ +static class Consts { + public const string AssemblyName = "MonoFN.Cecil"; + public const string PublicKey = "00240000048000009400000006020000002400005253413100040000010001002b5c9f7f04346c324a3176f8d3ee823bbf2d60efdbc35f86fd9e65ea3e6cd11bcdcba3a353e55133c8ac5c4caaba581b2c6dfff2cc2d0edc43959ddb86b973300a479a82419ef489c3225f1fe429a708507bd515835160e10bc743d20ca33ab9570cfd68d479fcf0bc797a763bec5d1000f0159ef619e709d915975e87beebaf"; +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Consts.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Consts.cs.meta new file mode 100644 index 0000000..2491449 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Consts.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: da413e0e98056364a9512beb26e9aea5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/CustomAttribute.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/CustomAttribute.cs new file mode 100644 index 0000000..d9bda73 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/CustomAttribute.cs @@ -0,0 +1,221 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System; +using System.Diagnostics; +using System.Threading; + +namespace MonoFN.Cecil { + + public struct CustomAttributeArgument { + + readonly TypeReference type; + readonly object value; + + public TypeReference Type { + get { return type; } + } + + public object Value { + get { return value; } + } + + public CustomAttributeArgument (TypeReference type, object value) + { + Mixin.CheckType (type); + this.type = type; + this.value = value; + } + } + + public struct CustomAttributeNamedArgument { + + readonly string name; + readonly CustomAttributeArgument argument; + + public string Name { + get { return name; } + } + + public CustomAttributeArgument Argument { + get { return argument; } + } + + public CustomAttributeNamedArgument (string name, CustomAttributeArgument argument) + { + Mixin.CheckName (name); + this.name = name; + this.argument = argument; + } + } + + public interface ICustomAttribute { + + TypeReference AttributeType { get; } + + bool HasFields { get; } + bool HasProperties { get; } + bool HasConstructorArguments { get; } + Collection Fields { get; } + Collection Properties { get; } + Collection ConstructorArguments { get; } + } + + [DebuggerDisplay ("{AttributeType}")] + public sealed class CustomAttribute : ICustomAttribute { + + internal CustomAttributeValueProjection projection; + readonly internal uint signature; + internal bool resolved; + MethodReference constructor; + byte [] blob; + internal Collection arguments; + internal Collection fields; + internal Collection properties; + + public MethodReference Constructor { + get { return constructor; } + set { constructor = value; } + } + + public TypeReference AttributeType { + get { return constructor.DeclaringType; } + } + + public bool IsResolved { + get { return resolved; } + } + + public bool HasConstructorArguments { + get { + Resolve (); + + return !arguments.IsNullOrEmpty (); + } + } + + public Collection ConstructorArguments { + get { + Resolve (); + + if (arguments == null) + Interlocked.CompareExchange (ref arguments, new Collection (), null); + + return arguments; + } + } + + public bool HasFields { + get { + Resolve (); + + return !fields.IsNullOrEmpty (); + } + } + + public Collection Fields { + get { + Resolve (); + + if (fields == null) + Interlocked.CompareExchange (ref fields, new Collection (), null); + + return fields; + } + } + + public bool HasProperties { + get { + Resolve (); + + return !properties.IsNullOrEmpty (); + } + } + + public Collection Properties { + get { + Resolve (); + + if (properties == null) + Interlocked.CompareExchange (ref properties, new Collection (), null); + + return properties; + } + } + + internal bool HasImage { + get { return constructor != null && constructor.HasImage; } + } + + internal ModuleDefinition Module { + get { return constructor.Module; } + } + + internal CustomAttribute (uint signature, MethodReference constructor) + { + this.signature = signature; + this.constructor = constructor; + this.resolved = false; + } + + public CustomAttribute (MethodReference constructor) + { + this.constructor = constructor; + this.resolved = true; + } + + public CustomAttribute (MethodReference constructor, byte [] blob) + { + this.constructor = constructor; + this.resolved = false; + this.blob = blob; + } + + public byte [] GetBlob () + { + if (blob != null) + return blob; + + if (!HasImage) + throw new NotSupportedException (); + + return Module.Read (ref blob, this, (attribute, reader) => reader.ReadCustomAttributeBlob (attribute.signature)); + } + + void Resolve () + { + if (resolved || !HasImage) + return; + + lock (Module.SyncRoot) { + if (resolved) + return; + + Module.Read (this, (attribute, reader) => { + try { + reader.ReadCustomAttributeSignature (attribute); + resolved = true; + } + catch (ResolutionException) { + if (arguments != null) + arguments.Clear (); + if (fields != null) + fields.Clear (); + if (properties != null) + properties.Clear (); + + resolved = false; + } + }); + } + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/CustomAttribute.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/CustomAttribute.cs.meta new file mode 100644 index 0000000..d60edbe --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/CustomAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 91db4bc8250b4b949bf262a196c9c0c9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/DefaultAssemblyResolver.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/DefaultAssemblyResolver.cs new file mode 100644 index 0000000..6bbcf96 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/DefaultAssemblyResolver.cs @@ -0,0 +1,61 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; +using System.Collections.Generic; + +namespace MonoFN.Cecil { + + public class DefaultAssemblyResolver : BaseAssemblyResolver { + + readonly IDictionary cache; + + public DefaultAssemblyResolver () + { + cache = new Dictionary (StringComparer.Ordinal); + } + + public override AssemblyDefinition Resolve (AssemblyNameReference name) + { + Mixin.CheckName (name); + + AssemblyDefinition assembly; + if (cache.TryGetValue (name.FullName, out assembly)) + return assembly; + + assembly = base.Resolve (name); + cache [name.FullName] = assembly; + + return assembly; + } + + protected void RegisterAssembly (AssemblyDefinition assembly) + { + if (assembly == null) + throw new ArgumentNullException ("assembly"); + + var name = assembly.Name.FullName; + if (cache.ContainsKey (name)) + return; + + cache [name] = assembly; + } + + protected override void Dispose (bool disposing) + { + foreach (var assembly in cache.Values) + assembly.Dispose (); + + cache.Clear (); + + base.Dispose (disposing); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/DefaultAssemblyResolver.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/DefaultAssemblyResolver.cs.meta new file mode 100644 index 0000000..75e60fb --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/DefaultAssemblyResolver.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5390b4f18cb83c046baaaa937fec06a7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EmbeddedResource.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EmbeddedResource.cs new file mode 100644 index 0000000..693283e --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EmbeddedResource.cs @@ -0,0 +1,98 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; +using System.IO; + +namespace MonoFN.Cecil { + + public sealed class EmbeddedResource : Resource { + + readonly MetadataReader reader; + + uint? offset; + byte [] data; + Stream stream; + + public override ResourceType ResourceType { + get { return ResourceType.Embedded; } + } + + public EmbeddedResource (string name, ManifestResourceAttributes attributes, byte [] data) : + base (name, attributes) + { + this.data = data; + } + + public EmbeddedResource (string name, ManifestResourceAttributes attributes, Stream stream) : + base (name, attributes) + { + this.stream = stream; + } + + internal EmbeddedResource (string name, ManifestResourceAttributes attributes, uint offset, MetadataReader reader) + : base (name, attributes) + { + this.offset = offset; + this.reader = reader; + } + + public Stream GetResourceStream () + { + if (stream != null) + return stream; + + if (data != null) + return new MemoryStream (data); + + if (offset.HasValue) + return new MemoryStream (reader.GetManagedResource (offset.Value)); + + throw new InvalidOperationException (); + } + + public byte [] GetResourceData () + { + if (stream != null) + return ReadStream (stream); + + if (data != null) + return data; + + if (offset.HasValue) + return reader.GetManagedResource (offset.Value); + + throw new InvalidOperationException (); + } + + static byte [] ReadStream (Stream stream) + { + int read; + + if (stream.CanSeek) { + var length = (int)stream.Length; + var data = new byte [length]; + int offset = 0; + + while ((read = stream.Read (data, offset, length - offset)) > 0) + offset += read; + + return data; + } + + var buffer = new byte [1024 * 8]; + var memory = new MemoryStream (); + while ((read = stream.Read (buffer, 0, buffer.Length)) > 0) + memory.Write (buffer, 0, read); + + return memory.ToArray (); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EmbeddedResource.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EmbeddedResource.cs.meta new file mode 100644 index 0000000..d7d8b36 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EmbeddedResource.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b5874acd8fee1b4499a3249dd4648428 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventAttributes.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventAttributes.cs new file mode 100644 index 0000000..7be266a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventAttributes.cs @@ -0,0 +1,21 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil { + + [Flags] + public enum EventAttributes : ushort { + None = 0x0000, + SpecialName = 0x0200, // Event is special + RTSpecialName = 0x0400 // CLI provides 'special' behavior, depending upon the name of the event + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventAttributes.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventAttributes.cs.meta new file mode 100644 index 0000000..11259a6 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventAttributes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 20c18be3d45bbf84d82732fd16751df3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventDefinition.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventDefinition.cs new file mode 100644 index 0000000..28776b5 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventDefinition.cs @@ -0,0 +1,156 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System.Threading; + +namespace MonoFN.Cecil { + + public sealed class EventDefinition : EventReference, IMemberDefinition { + + ushort attributes; + + Collection custom_attributes; + + internal MethodDefinition add_method; + internal MethodDefinition invoke_method; + internal MethodDefinition remove_method; + internal Collection other_methods; + + public EventAttributes Attributes { + get { return (EventAttributes)attributes; } + set { attributes = (ushort)value; } + } + + public MethodDefinition AddMethod { + get { + if (add_method != null) + return add_method; + + InitializeMethods (); + return add_method; + } + set { add_method = value; } + } + + public MethodDefinition InvokeMethod { + get { + if (invoke_method != null) + return invoke_method; + + InitializeMethods (); + return invoke_method; + } + set { invoke_method = value; } + } + + public MethodDefinition RemoveMethod { + get { + if (remove_method != null) + return remove_method; + + InitializeMethods (); + return remove_method; + } + set { remove_method = value; } + } + + public bool HasOtherMethods { + get { + if (other_methods != null) + return other_methods.Count > 0; + + InitializeMethods (); + return !other_methods.IsNullOrEmpty (); + } + } + + public Collection OtherMethods { + get { + if (other_methods != null) + return other_methods; + + InitializeMethods (); + + if (other_methods == null) + Interlocked.CompareExchange (ref other_methods, new Collection (), null); + + return other_methods; + } + } + + public bool HasCustomAttributes { + get { + if (custom_attributes != null) + return custom_attributes.Count > 0; + + return this.GetHasCustomAttributes (Module); + } + } + + public Collection CustomAttributes { + get { return custom_attributes ?? (this.GetCustomAttributes (ref custom_attributes, Module)); } + } + + #region EventAttributes + + public bool IsSpecialName { + get { return attributes.GetAttributes ((ushort)EventAttributes.SpecialName); } + set { attributes = attributes.SetAttributes ((ushort)EventAttributes.SpecialName, value); } + } + + public bool IsRuntimeSpecialName { + get { return attributes.GetAttributes ((ushort)EventAttributes.RTSpecialName); } + set { attributes = attributes.SetAttributes ((ushort)EventAttributes.RTSpecialName, value); } + } + + #endregion + + public new TypeDefinition DeclaringType { + get { return (TypeDefinition)base.DeclaringType; } + set { base.DeclaringType = value; } + } + + public override bool IsDefinition { + get { return true; } + } + + public EventDefinition (string name, EventAttributes attributes, TypeReference eventType) + : base (name, eventType) + { + this.attributes = (ushort)attributes; + this.token = new MetadataToken (TokenType.Event); + } + + void InitializeMethods () + { + var module = this.Module; + if (module == null) + return; + + lock (module.SyncRoot) { + if (add_method != null + || invoke_method != null + || remove_method != null) + return; + + if (!module.HasImage ()) + return; + + module.Read (this, (@event, reader) => reader.ReadMethods (@event)); + } + } + + public override EventDefinition Resolve () + { + return this; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventDefinition.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventDefinition.cs.meta new file mode 100644 index 0000000..c8daf6b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventDefinition.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4b201911ff1f925438edbe5b91275268 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventReference.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventReference.cs new file mode 100644 index 0000000..d188a37 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventReference.cs @@ -0,0 +1,40 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil { + + public abstract class EventReference : MemberReference { + + TypeReference event_type; + + public TypeReference EventType { + get { return event_type; } + set { event_type = value; } + } + + public override string FullName { + get { return event_type.FullName + " " + MemberFullName (); } + } + + protected EventReference (string name, TypeReference eventType) + : base (name) + { + Mixin.CheckType (eventType, Mixin.Argument.eventType); + event_type = eventType; + } + + protected override IMemberDefinition ResolveDefinition () + { + return this.Resolve (); + } + + public new abstract EventDefinition Resolve (); + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventReference.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventReference.cs.meta new file mode 100644 index 0000000..80e4e71 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventReference.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 94d9b58b6c4033343b975f6730127514 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ExportedType.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ExportedType.cs new file mode 100644 index 0000000..68b4410 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ExportedType.cs @@ -0,0 +1,238 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil { + + public sealed class ExportedType : IMetadataTokenProvider { + + string @namespace; + string name; + uint attributes; + IMetadataScope scope; + ModuleDefinition module; + int identifier; + ExportedType declaring_type; + internal MetadataToken token; + + public string Namespace { + get { return @namespace; } + set { @namespace = value; } + } + + public string Name { + get { return name; } + set { name = value; } + } + + public TypeAttributes Attributes { + get { return (TypeAttributes)attributes; } + set { attributes = (uint)value; } + } + + public IMetadataScope Scope { + get { + if (declaring_type != null) + return declaring_type.Scope; + + return scope; + } + set { + if (declaring_type != null) { + declaring_type.Scope = value; + return; + } + + scope = value; + } + } + + public ExportedType DeclaringType { + get { return declaring_type; } + set { declaring_type = value; } + } + + public MetadataToken MetadataToken { + get { return token; } + set { token = value; } + } + + public int Identifier { + get { return identifier; } + set { identifier = value; } + } + + #region TypeAttributes + + public bool IsNotPublic { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NotPublic); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NotPublic, value); } + } + + public bool IsPublic { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.Public); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.Public, value); } + } + + public bool IsNestedPublic { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedPublic); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedPublic, value); } + } + + public bool IsNestedPrivate { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedPrivate); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedPrivate, value); } + } + + public bool IsNestedFamily { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedFamily); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedFamily, value); } + } + + public bool IsNestedAssembly { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedAssembly); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedAssembly, value); } + } + + public bool IsNestedFamilyAndAssembly { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedFamANDAssem); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedFamANDAssem, value); } + } + + public bool IsNestedFamilyOrAssembly { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedFamORAssem); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedFamORAssem, value); } + } + + public bool IsAutoLayout { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.LayoutMask, (uint)TypeAttributes.AutoLayout); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.LayoutMask, (uint)TypeAttributes.AutoLayout, value); } + } + + public bool IsSequentialLayout { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.LayoutMask, (uint)TypeAttributes.SequentialLayout); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.LayoutMask, (uint)TypeAttributes.SequentialLayout, value); } + } + + public bool IsExplicitLayout { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.LayoutMask, (uint)TypeAttributes.ExplicitLayout); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.LayoutMask, (uint)TypeAttributes.ExplicitLayout, value); } + } + + public bool IsClass { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.ClassSemanticMask, (uint)TypeAttributes.Class); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.ClassSemanticMask, (uint)TypeAttributes.Class, value); } + } + + public bool IsInterface { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.ClassSemanticMask, (uint)TypeAttributes.Interface); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.ClassSemanticMask, (uint)TypeAttributes.Interface, value); } + } + + public bool IsAbstract { + get { return attributes.GetAttributes ((uint)TypeAttributes.Abstract); } + set { attributes = attributes.SetAttributes ((uint)TypeAttributes.Abstract, value); } + } + + public bool IsSealed { + get { return attributes.GetAttributes ((uint)TypeAttributes.Sealed); } + set { attributes = attributes.SetAttributes ((uint)TypeAttributes.Sealed, value); } + } + + public bool IsSpecialName { + get { return attributes.GetAttributes ((uint)TypeAttributes.SpecialName); } + set { attributes = attributes.SetAttributes ((uint)TypeAttributes.SpecialName, value); } + } + + public bool IsImport { + get { return attributes.GetAttributes ((uint)TypeAttributes.Import); } + set { attributes = attributes.SetAttributes ((uint)TypeAttributes.Import, value); } + } + + public bool IsSerializable { + get { return attributes.GetAttributes ((uint)TypeAttributes.Serializable); } + set { attributes = attributes.SetAttributes ((uint)TypeAttributes.Serializable, value); } + } + + public bool IsAnsiClass { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.StringFormatMask, (uint)TypeAttributes.AnsiClass); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.StringFormatMask, (uint)TypeAttributes.AnsiClass, value); } + } + + public bool IsUnicodeClass { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.StringFormatMask, (uint)TypeAttributes.UnicodeClass); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.StringFormatMask, (uint)TypeAttributes.UnicodeClass, value); } + } + + public bool IsAutoClass { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.StringFormatMask, (uint)TypeAttributes.AutoClass); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.StringFormatMask, (uint)TypeAttributes.AutoClass, value); } + } + + public bool IsBeforeFieldInit { + get { return attributes.GetAttributes ((uint)TypeAttributes.BeforeFieldInit); } + set { attributes = attributes.SetAttributes ((uint)TypeAttributes.BeforeFieldInit, value); } + } + + public bool IsRuntimeSpecialName { + get { return attributes.GetAttributes ((uint)TypeAttributes.RTSpecialName); } + set { attributes = attributes.SetAttributes ((uint)TypeAttributes.RTSpecialName, value); } + } + + public bool HasSecurity { + get { return attributes.GetAttributes ((uint)TypeAttributes.HasSecurity); } + set { attributes = attributes.SetAttributes ((uint)TypeAttributes.HasSecurity, value); } + } + + #endregion + + public bool IsForwarder { + get { return attributes.GetAttributes ((uint)TypeAttributes.Forwarder); } + set { attributes = attributes.SetAttributes ((uint)TypeAttributes.Forwarder, value); } + } + + public string FullName { + get { + var fullname = string.IsNullOrEmpty (@namespace) + ? name + : @namespace + '.' + name; + + if (declaring_type != null) + return declaring_type.FullName + "/" + fullname; + + return fullname; + } + } + + public ExportedType (string @namespace, string name, ModuleDefinition module, IMetadataScope scope) + { + this.@namespace = @namespace; + this.name = name; + this.scope = scope; + this.module = module; + } + + public override string ToString () + { + return FullName; + } + + public TypeDefinition Resolve () + { + return module.Resolve (CreateReference ()); + } + + internal TypeReference CreateReference () + { + return new TypeReference (@namespace, name, module, scope) { + DeclaringType = declaring_type != null ? declaring_type.CreateReference () : null, + }; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ExportedType.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ExportedType.cs.meta new file mode 100644 index 0000000..27b33ff --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ExportedType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b7fa6f0bdd43c0d44a63cd789a765eeb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldAttributes.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldAttributes.cs new file mode 100644 index 0000000..e4afd2c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldAttributes.cs @@ -0,0 +1,41 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil { + + [Flags] + public enum FieldAttributes : ushort { + FieldAccessMask = 0x0007, + CompilerControlled = 0x0000, // Member not referenceable + Private = 0x0001, // Accessible only by the parent type + FamANDAssem = 0x0002, // Accessible by sub-types only in this assembly + Assembly = 0x0003, // Accessible by anyone in the Assembly + Family = 0x0004, // Accessible only by type and sub-types + FamORAssem = 0x0005, // Accessible by sub-types anywhere, plus anyone in the assembly + Public = 0x0006, // Accessible by anyone who has visibility to this scope field contract attributes + + Static = 0x0010, // Defined on type, else per instance + InitOnly = 0x0020, // Field may only be initialized, not written after init + Literal = 0x0040, // Value is compile time constant + NotSerialized = 0x0080, // Field does not have to be serialized when type is remoted + SpecialName = 0x0200, // Field is special + + // Interop Attributes + PInvokeImpl = 0x2000, // Implementation is forwarded through PInvoke + + // Additional flags + RTSpecialName = 0x0400, // CLI provides 'special' behavior, depending upon the name of the field + HasFieldMarshal = 0x1000, // Field has marshalling information + HasDefault = 0x8000, // Field has default + HasFieldRVA = 0x0100 // Field has RVA + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldAttributes.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldAttributes.cs.meta new file mode 100644 index 0000000..767a61d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldAttributes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8e9187082e8fc1446a8c2aa12d8f3fa4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldDefinition.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldDefinition.cs new file mode 100644 index 0000000..73cdbc9 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldDefinition.cs @@ -0,0 +1,281 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System; + +namespace MonoFN.Cecil { + + public sealed class FieldDefinition : FieldReference, IMemberDefinition, IConstantProvider, IMarshalInfoProvider { + + ushort attributes; + Collection custom_attributes; + + int offset = Mixin.NotResolvedMarker; + + internal int rva = Mixin.NotResolvedMarker; + byte [] initial_value; + + object constant = Mixin.NotResolved; + + MarshalInfo marshal_info; + + void ResolveLayout () + { + if (offset != Mixin.NotResolvedMarker) + return; + + if (!HasImage) { + offset = Mixin.NoDataMarker; + return; + } + + lock (Module.SyncRoot) { + if (offset != Mixin.NotResolvedMarker) + return; + offset = Module.Read (this, (field, reader) => reader.ReadFieldLayout (field)); + } + } + + public bool HasLayoutInfo { + get { + if (offset >= 0) + return true; + + ResolveLayout (); + + return offset >= 0; + } + } + + public int Offset { + get { + if (offset >= 0) + return offset; + + ResolveLayout (); + + return offset >= 0 ? offset : -1; + } + set { offset = value; } + } + + internal FieldDefinitionProjection WindowsRuntimeProjection { + get { return (FieldDefinitionProjection)projection; } + set { projection = value; } + } + + void ResolveRVA () + { + if (rva != Mixin.NotResolvedMarker) + return; + + if (!HasImage) + return; + + lock (Module.SyncRoot) { + if (rva != Mixin.NotResolvedMarker) + return; + rva = Module.Read (this, (field, reader) => reader.ReadFieldRVA (field)); + } + } + + public int RVA { + get { + if (rva > 0) + return rva; + + ResolveRVA (); + + return rva > 0 ? rva : 0; + } + } + + public byte [] InitialValue { + get { + if (initial_value != null) + return initial_value; + + ResolveRVA (); + + if (initial_value == null) + initial_value = Empty.Array; + + return initial_value; + } + set { + initial_value = value; + HasFieldRVA = !initial_value.IsNullOrEmpty (); + rva = 0; + } + } + + public FieldAttributes Attributes { + get { return (FieldAttributes)attributes; } + set { + if (IsWindowsRuntimeProjection && (ushort)value != attributes) + throw new InvalidOperationException (); + + attributes = (ushort)value; + } + } + + public bool HasConstant { + get { + this.ResolveConstant (ref constant, Module); + + return constant != Mixin.NoValue; + } + set { if (!value) constant = Mixin.NoValue; } + } + + public object Constant { + get { return HasConstant ? constant : null; } + set { constant = value; } + } + + public bool HasCustomAttributes { + get { + if (custom_attributes != null) + return custom_attributes.Count > 0; + + return this.GetHasCustomAttributes (Module); + } + } + + public Collection CustomAttributes { + get { return custom_attributes ?? (this.GetCustomAttributes (ref custom_attributes, Module)); } + } + + public bool HasMarshalInfo { + get { + if (marshal_info != null) + return true; + + return this.GetHasMarshalInfo (Module); + } + } + + public MarshalInfo MarshalInfo { + get { return marshal_info ?? (this.GetMarshalInfo (ref marshal_info, Module)); } + set { marshal_info = value; } + } + + #region FieldAttributes + + public bool IsCompilerControlled { + get { return attributes.GetMaskedAttributes ((ushort)FieldAttributes.FieldAccessMask, (ushort)FieldAttributes.CompilerControlled); } + set { attributes = attributes.SetMaskedAttributes ((ushort)FieldAttributes.FieldAccessMask, (ushort)FieldAttributes.CompilerControlled, value); } + } + + public bool IsPrivate { + get { return attributes.GetMaskedAttributes ((ushort)FieldAttributes.FieldAccessMask, (ushort)FieldAttributes.Private); } + set { attributes = attributes.SetMaskedAttributes ((ushort)FieldAttributes.FieldAccessMask, (ushort)FieldAttributes.Private, value); } + } + + public bool IsFamilyAndAssembly { + get { return attributes.GetMaskedAttributes ((ushort)FieldAttributes.FieldAccessMask, (ushort)FieldAttributes.FamANDAssem); } + set { attributes = attributes.SetMaskedAttributes ((ushort)FieldAttributes.FieldAccessMask, (ushort)FieldAttributes.FamANDAssem, value); } + } + + public bool IsAssembly { + get { return attributes.GetMaskedAttributes ((ushort)FieldAttributes.FieldAccessMask, (ushort)FieldAttributes.Assembly); } + set { attributes = attributes.SetMaskedAttributes ((ushort)FieldAttributes.FieldAccessMask, (ushort)FieldAttributes.Assembly, value); } + } + + public bool IsFamily { + get { return attributes.GetMaskedAttributes ((ushort)FieldAttributes.FieldAccessMask, (ushort)FieldAttributes.Family); } + set { attributes = attributes.SetMaskedAttributes ((ushort)FieldAttributes.FieldAccessMask, (ushort)FieldAttributes.Family, value); } + } + + public bool IsFamilyOrAssembly { + get { return attributes.GetMaskedAttributes ((ushort)FieldAttributes.FieldAccessMask, (ushort)FieldAttributes.FamORAssem); } + set { attributes = attributes.SetMaskedAttributes ((ushort)FieldAttributes.FieldAccessMask, (ushort)FieldAttributes.FamORAssem, value); } + } + + public bool IsPublic { + get { return attributes.GetMaskedAttributes ((ushort)FieldAttributes.FieldAccessMask, (ushort)FieldAttributes.Public); } + set { attributes = attributes.SetMaskedAttributes ((ushort)FieldAttributes.FieldAccessMask, (ushort)FieldAttributes.Public, value); } + } + + public bool IsStatic { + get { return attributes.GetAttributes ((ushort)FieldAttributes.Static); } + set { attributes = attributes.SetAttributes ((ushort)FieldAttributes.Static, value); } + } + + public bool IsInitOnly { + get { return attributes.GetAttributes ((ushort)FieldAttributes.InitOnly); } + set { attributes = attributes.SetAttributes ((ushort)FieldAttributes.InitOnly, value); } + } + + public bool IsLiteral { + get { return attributes.GetAttributes ((ushort)FieldAttributes.Literal); } + set { attributes = attributes.SetAttributes ((ushort)FieldAttributes.Literal, value); } + } + + public bool IsNotSerialized { + get { return attributes.GetAttributes ((ushort)FieldAttributes.NotSerialized); } + set { attributes = attributes.SetAttributes ((ushort)FieldAttributes.NotSerialized, value); } + } + + public bool IsSpecialName { + get { return attributes.GetAttributes ((ushort)FieldAttributes.SpecialName); } + set { attributes = attributes.SetAttributes ((ushort)FieldAttributes.SpecialName, value); } + } + + public bool IsPInvokeImpl { + get { return attributes.GetAttributes ((ushort)FieldAttributes.PInvokeImpl); } + set { attributes = attributes.SetAttributes ((ushort)FieldAttributes.PInvokeImpl, value); } + } + + public bool IsRuntimeSpecialName { + get { return attributes.GetAttributes ((ushort)FieldAttributes.RTSpecialName); } + set { attributes = attributes.SetAttributes ((ushort)FieldAttributes.RTSpecialName, value); } + } + + public bool HasDefault { + get { return attributes.GetAttributes ((ushort)FieldAttributes.HasDefault); } + set { attributes = attributes.SetAttributes ((ushort)FieldAttributes.HasDefault, value); } + } + + public bool HasFieldRVA { + get { return attributes.GetAttributes ((ushort)FieldAttributes.HasFieldRVA); } + set { attributes = attributes.SetAttributes ((ushort)FieldAttributes.HasFieldRVA, value); } + } + + #endregion + + public override bool IsDefinition { + get { return true; } + } + + public new TypeDefinition DeclaringType { + get { return (TypeDefinition)base.DeclaringType; } + set { base.DeclaringType = value; } + } + + public FieldDefinition (string name, FieldAttributes attributes, TypeReference fieldType) + : base (name, fieldType) + { + this.attributes = (ushort)attributes; + } + + public override FieldDefinition Resolve () + { + return this; + } + } + + static partial class Mixin { + + public const int NotResolvedMarker = -2; + public const int NoDataMarker = -1; + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldDefinition.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldDefinition.cs.meta new file mode 100644 index 0000000..99dac2a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldDefinition.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 00dcb95135f39ec4c923f7923bcc93ed +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldReference.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldReference.cs new file mode 100644 index 0000000..1e9d3e3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldReference.cs @@ -0,0 +1,68 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil { + + public class FieldReference : MemberReference { + + TypeReference field_type; + + public TypeReference FieldType { + get { return field_type; } + set { field_type = value; } + } + + public override string FullName { + get { return field_type.FullName + " " + MemberFullName (); } + } + + public override bool ContainsGenericParameter { + get { return field_type.ContainsGenericParameter || base.ContainsGenericParameter; } + } + + internal FieldReference () + { + this.token = new MetadataToken (TokenType.MemberRef); + } + + public FieldReference (string name, TypeReference fieldType) + : base (name) + { + Mixin.CheckType (fieldType, Mixin.Argument.fieldType); + + this.field_type = fieldType; + this.token = new MetadataToken (TokenType.MemberRef); + } + + public FieldReference (string name, TypeReference fieldType, TypeReference declaringType) + : this (name, fieldType) + { + Mixin.CheckType (declaringType, Mixin.Argument.declaringType); + + this.DeclaringType = declaringType; + } + + protected override IMemberDefinition ResolveDefinition () + { + return this.Resolve (); + } + + public new virtual FieldDefinition Resolve () + { + var module = this.Module; + if (module == null) + throw new NotSupportedException (); + + return module.Resolve (this); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldReference.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldReference.cs.meta new file mode 100644 index 0000000..6f1c541 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldReference.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 990be2becde06b4468f79bd17646a47a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FileAttributes.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FileAttributes.cs new file mode 100644 index 0000000..32715d5 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FileAttributes.cs @@ -0,0 +1,17 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil { + + enum FileAttributes : uint { + ContainsMetaData = 0x0000, // This is not a resource file + ContainsNoMetaData = 0x0001, // This is a resource file or other non-metadata-containing file + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FileAttributes.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FileAttributes.cs.meta new file mode 100644 index 0000000..58c3fe0 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FileAttributes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 56e662d580c0894489669cc8058a2783 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FunctionPointerType.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FunctionPointerType.cs new file mode 100644 index 0000000..8558c54 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FunctionPointerType.cs @@ -0,0 +1,111 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System; +using System.Text; +using MD = MonoFN.Cecil.Metadata; + +namespace MonoFN.Cecil { + + public sealed class FunctionPointerType : TypeSpecification, IMethodSignature { + + readonly MethodReference function; + + public bool HasThis { + get { return function.HasThis; } + set { function.HasThis = value; } + } + + public bool ExplicitThis { + get { return function.ExplicitThis; } + set { function.ExplicitThis = value; } + } + + public MethodCallingConvention CallingConvention { + get { return function.CallingConvention; } + set { function.CallingConvention = value; } + } + + public bool HasParameters { + get { return function.HasParameters; } + } + + public Collection Parameters { + get { return function.Parameters; } + } + + public TypeReference ReturnType { + get { return function.MethodReturnType.ReturnType; } + set { function.MethodReturnType.ReturnType = value; } + } + + public MethodReturnType MethodReturnType { + get { return function.MethodReturnType; } + } + + public override string Name { + get { return function.Name; } + set { throw new InvalidOperationException (); } + } + + public override string Namespace { + get { return string.Empty; } + set { throw new InvalidOperationException (); } + } + + public override ModuleDefinition Module { + get { return ReturnType.Module; } + } + + public override IMetadataScope Scope { + get { return function.ReturnType.Scope; } + set { throw new InvalidOperationException (); } + } + + public override bool IsFunctionPointer { + get { return true; } + } + + public override bool ContainsGenericParameter { + get { return function.ContainsGenericParameter; } + } + + public override string FullName { + get { + var signature = new StringBuilder (); + signature.Append (function.Name); + signature.Append (" "); + signature.Append (function.ReturnType.FullName); + signature.Append (" *"); + this.MethodSignatureFullName (signature); + return signature.ToString (); + } + } + + public FunctionPointerType () + : base (null) + { + this.function = new MethodReference (); + this.function.Name = "method"; + this.etype = MD.ElementType.FnPtr; + } + + public override TypeDefinition Resolve () + { + return null; + } + + public override TypeReference GetElementType () + { + return this; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FunctionPointerType.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FunctionPointerType.cs.meta new file mode 100644 index 0000000..a576fd7 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FunctionPointerType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 91d84cd146416f945acbfce1b1dbcacb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericInstanceMethod.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericInstanceMethod.cs new file mode 100644 index 0000000..64737a9 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericInstanceMethod.cs @@ -0,0 +1,77 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System.Text; +using System.Threading; + +namespace MonoFN.Cecil { + + public sealed class GenericInstanceMethod : MethodSpecification, IGenericInstance, IGenericContext { + + Collection arguments; + + public bool HasGenericArguments { + get { return !arguments.IsNullOrEmpty (); } + } + + public Collection GenericArguments { + get { + if (arguments == null) + Interlocked.CompareExchange (ref arguments, new Collection (), null); + + return arguments; + } + } + + public override bool IsGenericInstance { + get { return true; } + } + + IGenericParameterProvider IGenericContext.Method { + get { return ElementMethod; } + } + + IGenericParameterProvider IGenericContext.Type { + get { return ElementMethod.DeclaringType; } + } + + public override bool ContainsGenericParameter { + get { return this.ContainsGenericParameter () || base.ContainsGenericParameter; } + } + + public override string FullName { + get { + var signature = new StringBuilder (); + var method = this.ElementMethod; + signature.Append (method.ReturnType.FullName) + .Append (" ") + .Append (method.DeclaringType.FullName) + .Append ("::") + .Append (method.Name); + this.GenericInstanceFullName (signature); + this.MethodSignatureFullName (signature); + return signature.ToString (); + + } + } + + public GenericInstanceMethod (MethodReference method) + : base (method) + { + } + + internal GenericInstanceMethod (MethodReference method, int arity) + : this (method) + { + this.arguments = new Collection (arity); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericInstanceMethod.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericInstanceMethod.cs.meta new file mode 100644 index 0000000..cee2434 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericInstanceMethod.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fd61b82dc3c653d42a1fc0f4e4c003f8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericInstanceType.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericInstanceType.cs new file mode 100644 index 0000000..4f40314 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericInstanceType.cs @@ -0,0 +1,75 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System; +using System.Text; +using System.Threading; +using MD = MonoFN.Cecil.Metadata; + +namespace MonoFN.Cecil { + + public sealed class GenericInstanceType : TypeSpecification, IGenericInstance, IGenericContext { + + Collection arguments; + + public bool HasGenericArguments { + get { return !arguments.IsNullOrEmpty (); } + } + + public Collection GenericArguments { + get { + if (arguments == null) + Interlocked.CompareExchange (ref arguments, new Collection (), null); + + return arguments; + } + } + + public override TypeReference DeclaringType { + get { return ElementType.DeclaringType; } + set { throw new NotSupportedException (); } + } + + public override string FullName { + get { + var name = new StringBuilder (); + name.Append (base.FullName); + this.GenericInstanceFullName (name); + return name.ToString (); + } + } + + public override bool IsGenericInstance { + get { return true; } + } + + public override bool ContainsGenericParameter { + get { return this.ContainsGenericParameter () || base.ContainsGenericParameter; } + } + + IGenericParameterProvider IGenericContext.Type { + get { return ElementType; } + } + + public GenericInstanceType (TypeReference type) + : base (type) + { + base.IsValueType = type.IsValueType; + this.etype = MD.ElementType.GenericInst; + } + + internal GenericInstanceType (TypeReference type, int arity) + : this (type) + { + this.arguments = new Collection (arity); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericInstanceType.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericInstanceType.cs.meta new file mode 100644 index 0000000..3d8278d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericInstanceType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 536e7e359f0fa6c449a352476e8af197 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameter.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameter.cs new file mode 100644 index 0000000..6b7ada6 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameter.cs @@ -0,0 +1,360 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.Metadata; +using MonoFN.Collections.Generic; +using System; +using System.Threading; + +namespace MonoFN.Cecil { + + public sealed class GenericParameter : TypeReference, ICustomAttributeProvider { + + internal int position; + internal GenericParameterType type; + internal IGenericParameterProvider owner; + + ushort attributes; + GenericParameterConstraintCollection constraints; + Collection custom_attributes; + + public GenericParameterAttributes Attributes { + get { return (GenericParameterAttributes)attributes; } + set { attributes = (ushort)value; } + } + + public int Position { + get { return position; } + } + + public GenericParameterType Type { + get { return type; } + } + + public IGenericParameterProvider Owner { + get { return owner; } + } + + public bool HasConstraints { + get { + if (constraints != null) + return constraints.Count > 0; + + return HasImage && Module.Read (this, (generic_parameter, reader) => reader.HasGenericConstraints (generic_parameter)); + } + } + + public Collection Constraints { + get { + if (constraints != null) + return constraints; + + if (HasImage) + return Module.Read (ref constraints, this, (generic_parameter, reader) => reader.ReadGenericConstraints (generic_parameter)); + + Interlocked.CompareExchange (ref constraints, new GenericParameterConstraintCollection (this), null); + return constraints; + } + } + + public bool HasCustomAttributes { + get { + if (custom_attributes != null) + return custom_attributes.Count > 0; + + return this.GetHasCustomAttributes (Module); + } + } + + public Collection CustomAttributes { + get { return custom_attributes ?? (this.GetCustomAttributes (ref custom_attributes, Module)); } + } + + public override IMetadataScope Scope { + get { + if (owner == null) + return null; + + return owner.GenericParameterType == GenericParameterType.Method + ? ((MethodReference)owner).DeclaringType.Scope + : ((TypeReference)owner).Scope; + } + set { throw new InvalidOperationException (); } + } + + public override TypeReference DeclaringType { + get { return owner as TypeReference; } + set { throw new InvalidOperationException (); } + } + + public MethodReference DeclaringMethod { + get { return owner as MethodReference; } + } + + public override ModuleDefinition Module { + get { return module ?? owner.Module; } + } + + public override string Name { + get { + if (!string.IsNullOrEmpty (base.Name)) + return base.Name; + + return base.Name = (type == GenericParameterType.Method ? "!!" : "!") + position; + } + } + + public override string Namespace { + get { return string.Empty; } + set { throw new InvalidOperationException (); } + } + + public override string FullName { + get { return Name; } + } + + public override bool IsGenericParameter { + get { return true; } + } + + public override bool ContainsGenericParameter { + get { return true; } + } + + public override MetadataType MetadataType { + get { return (MetadataType)etype; } + } + + #region GenericParameterAttributes + + public bool IsNonVariant { + get { return attributes.GetMaskedAttributes ((ushort)GenericParameterAttributes.VarianceMask, (ushort)GenericParameterAttributes.NonVariant); } + set { attributes = attributes.SetMaskedAttributes ((ushort)GenericParameterAttributes.VarianceMask, (ushort)GenericParameterAttributes.NonVariant, value); } + } + + public bool IsCovariant { + get { return attributes.GetMaskedAttributes ((ushort)GenericParameterAttributes.VarianceMask, (ushort)GenericParameterAttributes.Covariant); } + set { attributes = attributes.SetMaskedAttributes ((ushort)GenericParameterAttributes.VarianceMask, (ushort)GenericParameterAttributes.Covariant, value); } + } + + public bool IsContravariant { + get { return attributes.GetMaskedAttributes ((ushort)GenericParameterAttributes.VarianceMask, (ushort)GenericParameterAttributes.Contravariant); } + set { attributes = attributes.SetMaskedAttributes ((ushort)GenericParameterAttributes.VarianceMask, (ushort)GenericParameterAttributes.Contravariant, value); } + } + + public bool HasReferenceTypeConstraint { + get { return attributes.GetAttributes ((ushort)GenericParameterAttributes.ReferenceTypeConstraint); } + set { attributes = attributes.SetAttributes ((ushort)GenericParameterAttributes.ReferenceTypeConstraint, value); } + } + + public bool HasNotNullableValueTypeConstraint { + get { return attributes.GetAttributes ((ushort)GenericParameterAttributes.NotNullableValueTypeConstraint); } + set { attributes = attributes.SetAttributes ((ushort)GenericParameterAttributes.NotNullableValueTypeConstraint, value); } + } + + public bool HasDefaultConstructorConstraint { + get { return attributes.GetAttributes ((ushort)GenericParameterAttributes.DefaultConstructorConstraint); } + set { attributes = attributes.SetAttributes ((ushort)GenericParameterAttributes.DefaultConstructorConstraint, value); } + } + + #endregion + + public GenericParameter (IGenericParameterProvider owner) + : this (string.Empty, owner) + { + } + + public GenericParameter (string name, IGenericParameterProvider owner) + : base (string.Empty, name) + { + if (owner == null) + throw new ArgumentNullException (); + + this.position = -1; + this.owner = owner; + this.type = owner.GenericParameterType; + this.etype = ConvertGenericParameterType (this.type); + this.token = new MetadataToken (TokenType.GenericParam); + + } + + internal GenericParameter (int position, GenericParameterType type, ModuleDefinition module) + : base (string.Empty, string.Empty) + { + Mixin.CheckModule (module); + + this.position = position; + this.type = type; + this.etype = ConvertGenericParameterType (type); + this.module = module; + this.token = new MetadataToken (TokenType.GenericParam); + } + + static ElementType ConvertGenericParameterType (GenericParameterType type) + { + switch (type) { + case GenericParameterType.Type: + return ElementType.Var; + case GenericParameterType.Method: + return ElementType.MVar; + } + + throw new ArgumentOutOfRangeException (); + } + + public override TypeDefinition Resolve () + { + return null; + } + } + + sealed class GenericParameterCollection : Collection { + + readonly IGenericParameterProvider owner; + + internal GenericParameterCollection (IGenericParameterProvider owner) + { + this.owner = owner; + } + + internal GenericParameterCollection (IGenericParameterProvider owner, int capacity) + : base (capacity) + { + this.owner = owner; + } + + protected override void OnAdd (GenericParameter item, int index) + { + UpdateGenericParameter (item, index); + } + + protected override void OnInsert (GenericParameter item, int index) + { + UpdateGenericParameter (item, index); + + for (int i = index; i < size; i++) + items [i].position = i + 1; + } + + protected override void OnSet (GenericParameter item, int index) + { + UpdateGenericParameter (item, index); + } + + void UpdateGenericParameter (GenericParameter item, int index) + { + item.owner = owner; + item.position = index; + item.type = owner.GenericParameterType; + } + + protected override void OnRemove (GenericParameter item, int index) + { + item.owner = null; + item.position = -1; + item.type = GenericParameterType.Type; + + for (int i = index + 1; i < size; i++) + items [i].position = i - 1; + } + } + + public sealed class GenericParameterConstraint : ICustomAttributeProvider { + + internal GenericParameter generic_parameter; + internal MetadataToken token; + + TypeReference constraint_type; + Collection custom_attributes; + + public TypeReference ConstraintType { + get { return constraint_type; } + set { constraint_type = value; } + } + + public bool HasCustomAttributes { + get { + if (custom_attributes != null) + return custom_attributes.Count > 0; + + if (generic_parameter == null) + return false; + + return this.GetHasCustomAttributes (generic_parameter.Module); + } + } + + public Collection CustomAttributes { + get { + if (generic_parameter == null) { + if (custom_attributes == null) + Interlocked.CompareExchange (ref custom_attributes, new Collection (), null); + return custom_attributes; + } + + return custom_attributes ?? (this.GetCustomAttributes (ref custom_attributes, generic_parameter.Module)); + } + } + + public MetadataToken MetadataToken { + get { return token; } + set { token = value; } + } + + internal GenericParameterConstraint (TypeReference constraintType, MetadataToken token) + { + this.constraint_type = constraintType; + this.token = token; + } + + public GenericParameterConstraint (TypeReference constraintType) + { + Mixin.CheckType (constraintType, Mixin.Argument.constraintType); + + this.constraint_type = constraintType; + this.token = new MetadataToken (TokenType.GenericParamConstraint); + } + } + + class GenericParameterConstraintCollection : Collection { + readonly GenericParameter generic_parameter; + + internal GenericParameterConstraintCollection (GenericParameter genericParameter) + { + this.generic_parameter = genericParameter; + } + + internal GenericParameterConstraintCollection (GenericParameter genericParameter, int length) + : base (length) + { + this.generic_parameter = genericParameter; + } + + protected override void OnAdd (GenericParameterConstraint item, int index) + { + item.generic_parameter = generic_parameter; + } + + protected override void OnInsert (GenericParameterConstraint item, int index) + { + item.generic_parameter = generic_parameter; + } + + protected override void OnSet (GenericParameterConstraint item, int index) + { + item.generic_parameter = generic_parameter; + } + + protected override void OnRemove (GenericParameterConstraint item, int index) + { + item.generic_parameter = null; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameter.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameter.cs.meta new file mode 100644 index 0000000..73db53b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a20d39eab5fe7884c8cdf55fde143904 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameterAttributes.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameterAttributes.cs new file mode 100644 index 0000000..a5ae0fc --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameterAttributes.cs @@ -0,0 +1,27 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil { + + [Flags] + public enum GenericParameterAttributes : ushort { + VarianceMask = 0x0003, + NonVariant = 0x0000, + Covariant = 0x0001, + Contravariant = 0x0002, + + SpecialConstraintMask = 0x001c, + ReferenceTypeConstraint = 0x0004, + NotNullableValueTypeConstraint = 0x0008, + DefaultConstructorConstraint = 0x0010 + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameterAttributes.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameterAttributes.cs.meta new file mode 100644 index 0000000..b4e1bf9 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameterAttributes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 406369465fc07a643b3923d53fb1e941 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameterResolver.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameterResolver.cs new file mode 100644 index 0000000..b3677e6 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameterResolver.cs @@ -0,0 +1,175 @@ +using MonoFN.Cecil.Cil; +using System; + +namespace MonoFN.Cecil { + internal sealed class GenericParameterResolver { + internal static TypeReference ResolveReturnTypeIfNeeded (MethodReference methodReference) + { + if (methodReference.DeclaringType.IsArray && methodReference.Name == "Get") + return methodReference.ReturnType; + + var genericInstanceMethod = methodReference as GenericInstanceMethod; + var declaringGenericInstanceType = methodReference.DeclaringType as GenericInstanceType; + + if (genericInstanceMethod == null && declaringGenericInstanceType == null) + return methodReference.ReturnType; + + return ResolveIfNeeded (genericInstanceMethod, declaringGenericInstanceType, methodReference.ReturnType); + } + + internal static TypeReference ResolveFieldTypeIfNeeded (FieldReference fieldReference) + { + return ResolveIfNeeded (null, fieldReference.DeclaringType as GenericInstanceType, fieldReference.FieldType); + } + + internal static TypeReference ResolveParameterTypeIfNeeded (MethodReference method, ParameterReference parameter) + { + var genericInstanceMethod = method as GenericInstanceMethod; + var declaringGenericInstanceType = method.DeclaringType as GenericInstanceType; + + if (genericInstanceMethod == null && declaringGenericInstanceType == null) + return parameter.ParameterType; + + return ResolveIfNeeded (genericInstanceMethod, declaringGenericInstanceType, parameter.ParameterType); + } + + internal static TypeReference ResolveVariableTypeIfNeeded (MethodReference method, VariableReference variable) + { + var genericInstanceMethod = method as GenericInstanceMethod; + var declaringGenericInstanceType = method.DeclaringType as GenericInstanceType; + + if (genericInstanceMethod == null && declaringGenericInstanceType == null) + return variable.VariableType; + + return ResolveIfNeeded (genericInstanceMethod, declaringGenericInstanceType, variable.VariableType); + } + + private static TypeReference ResolveIfNeeded (IGenericInstance genericInstanceMethod, IGenericInstance declaringGenericInstanceType, TypeReference parameterType) + { + var byRefType = parameterType as ByReferenceType; + if (byRefType != null) + return ResolveIfNeeded (genericInstanceMethod, declaringGenericInstanceType, byRefType); + + var arrayType = parameterType as ArrayType; + if (arrayType != null) + return ResolveIfNeeded (genericInstanceMethod, declaringGenericInstanceType, arrayType); + + var genericInstanceType = parameterType as GenericInstanceType; + if (genericInstanceType != null) + return ResolveIfNeeded (genericInstanceMethod, declaringGenericInstanceType, genericInstanceType); + + var genericParameter = parameterType as GenericParameter; + if (genericParameter != null) + return ResolveIfNeeded (genericInstanceMethod, declaringGenericInstanceType, genericParameter); + + var requiredModifierType = parameterType as RequiredModifierType; + if (requiredModifierType != null && ContainsGenericParameters (requiredModifierType)) + return ResolveIfNeeded (genericInstanceMethod, declaringGenericInstanceType, requiredModifierType.ElementType); + + if (ContainsGenericParameters (parameterType)) + throw new Exception ("Unexpected generic parameter."); + + return parameterType; + } + + private static TypeReference ResolveIfNeeded (IGenericInstance genericInstanceMethod, IGenericInstance genericInstanceType, GenericParameter genericParameterElement) + { + return (genericParameterElement.MetadataType == MetadataType.MVar) + ? (genericInstanceMethod != null ? genericInstanceMethod.GenericArguments [genericParameterElement.Position] : genericParameterElement) + : genericInstanceType.GenericArguments [genericParameterElement.Position]; + } + + private static ArrayType ResolveIfNeeded (IGenericInstance genericInstanceMethod, IGenericInstance genericInstanceType, ArrayType arrayType) + { + return new ArrayType (ResolveIfNeeded (genericInstanceMethod, genericInstanceType, arrayType.ElementType), arrayType.Rank); + } + + private static ByReferenceType ResolveIfNeeded (IGenericInstance genericInstanceMethod, IGenericInstance genericInstanceType, ByReferenceType byReferenceType) + { + return new ByReferenceType (ResolveIfNeeded (genericInstanceMethod, genericInstanceType, byReferenceType.ElementType)); + } + + private static GenericInstanceType ResolveIfNeeded (IGenericInstance genericInstanceMethod, IGenericInstance genericInstanceType, GenericInstanceType genericInstanceType1) + { + if (!ContainsGenericParameters (genericInstanceType1)) + return genericInstanceType1; + + var newGenericInstance = new GenericInstanceType (genericInstanceType1.ElementType); + + foreach (var genericArgument in genericInstanceType1.GenericArguments) { + if (!genericArgument.IsGenericParameter) { + newGenericInstance.GenericArguments.Add (ResolveIfNeeded (genericInstanceMethod, genericInstanceType, genericArgument)); + continue; + } + + var genParam = (GenericParameter)genericArgument; + + switch (genParam.Type) { + case GenericParameterType.Type: { + if (genericInstanceType == null) + throw new NotSupportedException (); + + newGenericInstance.GenericArguments.Add (genericInstanceType.GenericArguments [genParam.Position]); + } + break; + + case GenericParameterType.Method: { + if (genericInstanceMethod == null) + newGenericInstance.GenericArguments.Add (genParam); + else + newGenericInstance.GenericArguments.Add (genericInstanceMethod.GenericArguments [genParam.Position]); + } + break; + } + } + + return newGenericInstance; + } + + private static bool ContainsGenericParameters (TypeReference typeReference) + { + var genericParameter = typeReference as GenericParameter; + if (genericParameter != null) + return true; + + var arrayType = typeReference as ArrayType; + if (arrayType != null) + return ContainsGenericParameters (arrayType.ElementType); + + var pointerType = typeReference as PointerType; + if (pointerType != null) + return ContainsGenericParameters (pointerType.ElementType); + + var byRefType = typeReference as ByReferenceType; + if (byRefType != null) + return ContainsGenericParameters (byRefType.ElementType); + + var sentinelType = typeReference as SentinelType; + if (sentinelType != null) + return ContainsGenericParameters (sentinelType.ElementType); + + var pinnedType = typeReference as PinnedType; + if (pinnedType != null) + return ContainsGenericParameters (pinnedType.ElementType); + + var requiredModifierType = typeReference as RequiredModifierType; + if (requiredModifierType != null) + return ContainsGenericParameters (requiredModifierType.ElementType); + + var genericInstance = typeReference as GenericInstanceType; + if (genericInstance != null) { + foreach (var genericArgument in genericInstance.GenericArguments) { + if (ContainsGenericParameters (genericArgument)) + return true; + } + + return false; + } + + if (typeReference is TypeSpecification) + throw new NotSupportedException (); + + return false; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameterResolver.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameterResolver.cs.meta new file mode 100644 index 0000000..c982dd5 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameterResolver.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4151464170fe40440a031e1f4f2ecf53 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IConstantProvider.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IConstantProvider.cs new file mode 100644 index 0000000..3631258 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IConstantProvider.cs @@ -0,0 +1,44 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil { + + public interface IConstantProvider : IMetadataTokenProvider { + + bool HasConstant { get; set; } + object Constant { get; set; } + } + + static partial class Mixin { + + internal static object NoValue = new object (); + internal static object NotResolved = new object (); + + public static void ResolveConstant ( + this IConstantProvider self, + ref object constant, + ModuleDefinition module) + { + if (module == null) { + constant = Mixin.NoValue; + return; + } + + lock (module.SyncRoot) { + if (constant != Mixin.NotResolved) + return; + if (module.HasImage ()) + constant = module.Read (self, (provider, reader) => reader.ReadConstant (provider)); + else + constant = Mixin.NoValue; + } + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IConstantProvider.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IConstantProvider.cs.meta new file mode 100644 index 0000000..8f377d7 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IConstantProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 58315b928b9e49540b9d1591523c92c6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ICustomAttributeProvider.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ICustomAttributeProvider.cs new file mode 100644 index 0000000..39b8f01 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ICustomAttributeProvider.cs @@ -0,0 +1,44 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System.Threading; + +namespace MonoFN.Cecil { + + public interface ICustomAttributeProvider : IMetadataTokenProvider { + + Collection CustomAttributes { get; } + + bool HasCustomAttributes { get; } + } + + static partial class Mixin { + + public static bool GetHasCustomAttributes ( + this ICustomAttributeProvider self, + ModuleDefinition module) + { + return module.HasImage () && module.Read (self, (provider, reader) => reader.HasCustomAttributes (provider)); + } + + public static Collection GetCustomAttributes ( + this ICustomAttributeProvider self, + ref Collection variable, + ModuleDefinition module) + { + if (module.HasImage ()) + return module.Read (ref variable, self, (provider, reader) => reader.ReadCustomAttributes (provider)); + + Interlocked.CompareExchange (ref variable, new Collection (), null); + return variable; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ICustomAttributeProvider.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ICustomAttributeProvider.cs.meta new file mode 100644 index 0000000..e61f1f9 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ICustomAttributeProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1585603aa3fe4f1409c42694378a557c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IGenericInstance.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IGenericInstance.cs new file mode 100644 index 0000000..ecee2cc --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IGenericInstance.cs @@ -0,0 +1,47 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System.Text; + +namespace MonoFN.Cecil { + + public interface IGenericInstance : IMetadataTokenProvider { + + bool HasGenericArguments { get; } + Collection GenericArguments { get; } + } + + static partial class Mixin { + + public static bool ContainsGenericParameter (this IGenericInstance self) + { + var arguments = self.GenericArguments; + + for (int i = 0; i < arguments.Count; i++) + if (arguments [i].ContainsGenericParameter) + return true; + + return false; + } + + public static void GenericInstanceFullName (this IGenericInstance self, StringBuilder builder) + { + builder.Append ("<"); + var arguments = self.GenericArguments; + for (int i = 0; i < arguments.Count; i++) { + if (i > 0) + builder.Append (","); + builder.Append (arguments [i].FullName); + } + builder.Append (">"); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IGenericInstance.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IGenericInstance.cs.meta new file mode 100644 index 0000000..3907d65 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IGenericInstance.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 22b3a8ddd25a989449e5e9ffc632bc00 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IGenericParameterProvider.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IGenericParameterProvider.cs new file mode 100644 index 0000000..da27ad3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IGenericParameterProvider.cs @@ -0,0 +1,58 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System.Threading; + +namespace MonoFN.Cecil { + + public interface IGenericParameterProvider : IMetadataTokenProvider { + + bool HasGenericParameters { get; } + bool IsDefinition { get; } + ModuleDefinition Module { get; } + Collection GenericParameters { get; } + GenericParameterType GenericParameterType { get; } + } + + public enum GenericParameterType { + Type, + Method + } + + interface IGenericContext { + + bool IsDefinition { get; } + IGenericParameterProvider Type { get; } + IGenericParameterProvider Method { get; } + } + + static partial class Mixin { + + public static bool GetHasGenericParameters ( + this IGenericParameterProvider self, + ModuleDefinition module) + { + return module.HasImage () && module.Read (self, (provider, reader) => reader.HasGenericParameters (provider)); + } + + public static Collection GetGenericParameters ( + this IGenericParameterProvider self, + ref Collection collection, + ModuleDefinition module) + { + if (module.HasImage ()) + return module.Read (ref collection, self, (provider, reader) => reader.ReadGenericParameters (provider)); + + Interlocked.CompareExchange (ref collection, new GenericParameterCollection (self), null); + return collection; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IGenericParameterProvider.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IGenericParameterProvider.cs.meta new file mode 100644 index 0000000..d11d967 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IGenericParameterProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c81842a9d1ff34f4b94b047da954469f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMarshalInfoProvider.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMarshalInfoProvider.cs new file mode 100644 index 0000000..75faa1a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMarshalInfoProvider.cs @@ -0,0 +1,38 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil { + + public interface IMarshalInfoProvider : IMetadataTokenProvider { + + bool HasMarshalInfo { get; } + MarshalInfo MarshalInfo { get; set; } + } + + static partial class Mixin { + + public static bool GetHasMarshalInfo ( + this IMarshalInfoProvider self, + ModuleDefinition module) + { + return module.HasImage () && module.Read (self, (provider, reader) => reader.HasMarshalInfo (provider)); + } + + public static MarshalInfo GetMarshalInfo ( + this IMarshalInfoProvider self, + ref MarshalInfo variable, + ModuleDefinition module) + { + return module.HasImage () + ? module.Read (ref variable, self, (provider, reader) => reader.ReadMarshalInfo (provider)) + : null; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMarshalInfoProvider.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMarshalInfoProvider.cs.meta new file mode 100644 index 0000000..8ce8f0c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMarshalInfoProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a73829f4e35521e4892ef4e0cce2e4f9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMemberDefinition.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMemberDefinition.cs new file mode 100644 index 0000000..743e8d4 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMemberDefinition.cs @@ -0,0 +1,82 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil { + + public interface IMemberDefinition : ICustomAttributeProvider { + + string Name { get; set; } + string FullName { get; } + + bool IsSpecialName { get; set; } + bool IsRuntimeSpecialName { get; set; } + + TypeDefinition DeclaringType { get; set; } + } + + static partial class Mixin { + + public static bool GetAttributes (this uint self, uint attributes) + { + return (self & attributes) != 0; + } + + public static uint SetAttributes (this uint self, uint attributes, bool value) + { + if (value) + return self | attributes; + + return self & ~attributes; + } + + public static bool GetMaskedAttributes (this uint self, uint mask, uint attributes) + { + return (self & mask) == attributes; + } + + public static uint SetMaskedAttributes (this uint self, uint mask, uint attributes, bool value) + { + if (value) { + self &= ~mask; + return self | attributes; + } + + return self & ~(mask & attributes); + } + + public static bool GetAttributes (this ushort self, ushort attributes) + { + return (self & attributes) != 0; + } + + public static ushort SetAttributes (this ushort self, ushort attributes, bool value) + { + if (value) + return (ushort)(self | attributes); + + return (ushort)(self & ~attributes); + } + + public static bool GetMaskedAttributes (this ushort self, ushort mask, uint attributes) + { + return (self & mask) == attributes; + } + + public static ushort SetMaskedAttributes (this ushort self, ushort mask, uint attributes, bool value) + { + if (value) { + self = (ushort)(self & ~mask); + return (ushort)(self | attributes); + } + + return (ushort)(self & ~(mask & attributes)); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMemberDefinition.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMemberDefinition.cs.meta new file mode 100644 index 0000000..b78f8dc --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMemberDefinition.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 482577ba8693d3f438504e54d6edb971 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMetadataScope.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMetadataScope.cs new file mode 100644 index 0000000..7604a9e --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMetadataScope.cs @@ -0,0 +1,23 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil { + + public enum MetadataScopeType { + AssemblyNameReference, + ModuleReference, + ModuleDefinition, + } + + public interface IMetadataScope : IMetadataTokenProvider { + MetadataScopeType MetadataScopeType { get; } + string Name { get; set; } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMetadataScope.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMetadataScope.cs.meta new file mode 100644 index 0000000..0b8b2bc --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMetadataScope.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: da1ce103d170a414c8c8c6849a8856a3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMetadataTokenProvider.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMetadataTokenProvider.cs new file mode 100644 index 0000000..a82c759 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMetadataTokenProvider.cs @@ -0,0 +1,17 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil { + + public interface IMetadataTokenProvider { + + MetadataToken MetadataToken { get; set; } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMetadataTokenProvider.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMetadataTokenProvider.cs.meta new file mode 100644 index 0000000..eb83452 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMetadataTokenProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c01133d3a99e95542b153987746696d1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMethodSignature.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMethodSignature.cs new file mode 100644 index 0000000..57be39c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMethodSignature.cs @@ -0,0 +1,56 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System.Text; + +namespace MonoFN.Cecil { + + public interface IMethodSignature : IMetadataTokenProvider { + + bool HasThis { get; set; } + bool ExplicitThis { get; set; } + MethodCallingConvention CallingConvention { get; set; } + + bool HasParameters { get; } + Collection Parameters { get; } + TypeReference ReturnType { get; set; } + MethodReturnType MethodReturnType { get; } + } + + static partial class Mixin { + + public static bool HasImplicitThis (this IMethodSignature self) + { + return self.HasThis && !self.ExplicitThis; + } + + public static void MethodSignatureFullName (this IMethodSignature self, StringBuilder builder) + { + builder.Append ("("); + + if (self.HasParameters) { + var parameters = self.Parameters; + for (int i = 0; i < parameters.Count; i++) { + var parameter = parameters [i]; + if (i > 0) + builder.Append (","); + + if (parameter.ParameterType.IsSentinel) + builder.Append ("...,"); + + builder.Append (parameter.ParameterType.FullName); + } + } + + builder.Append (")"); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMethodSignature.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMethodSignature.cs.meta new file mode 100644 index 0000000..2f3a715 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMethodSignature.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8fdcc55987f0a4f4b93358d994704a98 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Import.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Import.cs new file mode 100644 index 0000000..e021176 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Import.cs @@ -0,0 +1,858 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.Metadata; +using MonoFN.Collections.Generic; +using System; +using System.Collections.Generic; +using SR = System.Reflection; + +namespace MonoFN.Cecil +{ + + public interface IMetadataImporterProvider + { + IMetadataImporter GetMetadataImporter(ModuleDefinition module); + } + + public interface IMetadataImporter + { + AssemblyNameReference ImportReference(AssemblyNameReference reference); + TypeReference ImportReference(TypeReference type, IGenericParameterProvider context); + FieldReference ImportReference(FieldReference field, IGenericParameterProvider context); + MethodReference ImportReference(MethodReference method, IGenericParameterProvider context); + } + + public interface IReflectionImporterProvider + { + IReflectionImporter GetReflectionImporter(ModuleDefinition module); + } + + public interface IReflectionImporter + { + AssemblyNameReference ImportReference(SR.AssemblyName reference); + TypeReference ImportReference(Type type, IGenericParameterProvider context); + FieldReference ImportReference(SR.FieldInfo field, IGenericParameterProvider context); + MethodReference ImportReference(SR.MethodBase method, IGenericParameterProvider context); + } + + struct ImportGenericContext + { + + Collection stack; + + public bool IsEmpty { get { return stack == null; } } + + public ImportGenericContext(IGenericParameterProvider provider) + { + if (provider == null) + throw new ArgumentNullException("provider"); + + stack = null; + + Push(provider); + } + + public void Push(IGenericParameterProvider provider) + { + if (stack == null) + stack = new Collection(1) { provider }; + else + stack.Add(provider); + } + + public void Pop() + { + stack.RemoveAt(stack.Count - 1); + } + + public TypeReference MethodParameter(string method, int position) + { + for (int i = stack.Count - 1; i >= 0; i--) + { + var candidate = stack[i] as MethodReference; + if (candidate == null) + continue; + + if (method != NormalizeMethodName(candidate)) + continue; + + return candidate.GenericParameters[position]; + } + + throw new InvalidOperationException(); + } + + public string NormalizeMethodName(MethodReference method) + { + return method.DeclaringType.GetElementType().FullName + "." + method.Name; + } + + public TypeReference TypeParameter(string type, int position) + { + for (int i = stack.Count - 1; i >= 0; i--) + { + var candidate = GenericTypeFor(stack[i]); + + if (candidate.FullName != type) + continue; + + return candidate.GenericParameters[position]; + } + + throw new InvalidOperationException(); + } + + static TypeReference GenericTypeFor(IGenericParameterProvider context) + { + var type = context as TypeReference; + if (type != null) + return type.GetElementType(); + + var method = context as MethodReference; + if (method != null) + return method.DeclaringType.GetElementType(); + + throw new InvalidOperationException(); + } + + public static ImportGenericContext For(IGenericParameterProvider context) + { + return context != null ? new ImportGenericContext(context) : default(ImportGenericContext); + } + } + + public class DefaultReflectionImporter : IReflectionImporter + { + + readonly protected ModuleDefinition module; + + public DefaultReflectionImporter(ModuleDefinition module) + { + Mixin.CheckModule(module); + this.module = module; + } + + enum ImportGenericKind + { + Definition, + Open, + } + + static readonly Dictionary type_etype_mapping = new Dictionary(18) { + { typeof (void), ElementType.Void }, + { typeof (bool), ElementType.Boolean }, + { typeof (char), ElementType.Char }, + { typeof (sbyte), ElementType.I1 }, + { typeof (byte), ElementType.U1 }, + { typeof (short), ElementType.I2 }, + { typeof (ushort), ElementType.U2 }, + { typeof (int), ElementType.I4 }, + { typeof (uint), ElementType.U4 }, + { typeof (long), ElementType.I8 }, + { typeof (ulong), ElementType.U8 }, + { typeof (float), ElementType.R4 }, + { typeof (double), ElementType.R8 }, + { typeof (string), ElementType.String }, + { typeof (TypedReference), ElementType.TypedByRef }, + { typeof (IntPtr), ElementType.I }, + { typeof (UIntPtr), ElementType.U }, + { typeof (object), ElementType.Object }, + }; + + TypeReference ImportType(Type type, ImportGenericContext context) + { + return ImportType(type, context, ImportGenericKind.Open); + } + + TypeReference ImportType(Type type, ImportGenericContext context, ImportGenericKind import_kind) + { + if (IsTypeSpecification(type) || ImportOpenGenericType(type, import_kind)) + return ImportTypeSpecification(type, context); + + var reference = new TypeReference( + string.Empty, + type.Name, + module, + ImportScope(type), + type.IsValueType); + + reference.etype = ImportElementType(type); + + if (IsNestedType(type)) + reference.DeclaringType = ImportType(type.DeclaringType, context, import_kind); + else + reference.Namespace = type.Namespace ?? string.Empty; + + if (type.IsGenericType) + ImportGenericParameters(reference, type.GetGenericArguments()); + + return reference; + } + + protected virtual IMetadataScope ImportScope(Type type) + { + return ImportScope(type.Assembly); + } + + static bool ImportOpenGenericType(Type type, ImportGenericKind import_kind) + { + return type.IsGenericType && type.IsGenericTypeDefinition && import_kind == ImportGenericKind.Open; + } + + static bool ImportOpenGenericMethod(SR.MethodBase method, ImportGenericKind import_kind) + { + return method.IsGenericMethod && method.IsGenericMethodDefinition && import_kind == ImportGenericKind.Open; + } + + static bool IsNestedType(Type type) + { + return type.IsNested; + } + + TypeReference ImportTypeSpecification(Type type, ImportGenericContext context) + { + if (type.IsByRef) + return new ByReferenceType(ImportType(type.GetElementType(), context)); + + if (type.IsPointer) + return new PointerType(ImportType(type.GetElementType(), context)); + + if (type.IsArray) + return new ArrayType(ImportType(type.GetElementType(), context), type.GetArrayRank()); + + if (type.IsGenericType) + return ImportGenericInstance(type, context); + + if (type.IsGenericParameter) + return ImportGenericParameter(type, context); + + throw new NotSupportedException(type.FullName); + } + + static TypeReference ImportGenericParameter(Type type, ImportGenericContext context) + { + if (context.IsEmpty) + throw new InvalidOperationException(); + + if (type.DeclaringMethod != null) + return context.MethodParameter(NormalizeMethodName(type.DeclaringMethod), type.GenericParameterPosition); + + if (type.DeclaringType != null) + return context.TypeParameter(NormalizeTypeFullName(type.DeclaringType), type.GenericParameterPosition); + + throw new InvalidOperationException(); + } + + static string NormalizeMethodName(SR.MethodBase method) + { + return NormalizeTypeFullName(method.DeclaringType) + "." + method.Name; + } + + static string NormalizeTypeFullName(Type type) + { + if (IsNestedType(type)) + return NormalizeTypeFullName(type.DeclaringType) + "/" + type.Name; + + return type.FullName; + } + + TypeReference ImportGenericInstance(Type type, ImportGenericContext context) + { + var element_type = ImportType(type.GetGenericTypeDefinition(), context, ImportGenericKind.Definition); + var arguments = type.GetGenericArguments(); + var instance = new GenericInstanceType(element_type, arguments.Length); + var instance_arguments = instance.GenericArguments; + + context.Push(element_type); + try + { + for (int i = 0; i < arguments.Length; i++) + instance_arguments.Add(ImportType(arguments[i], context)); + + return instance; + } + finally + { + context.Pop(); + } + } + + static bool IsTypeSpecification(Type type) + { + return type.HasElementType + || IsGenericInstance(type) + || type.IsGenericParameter; + } + + static bool IsGenericInstance(Type type) + { + return type.IsGenericType && !type.IsGenericTypeDefinition; + } + + static ElementType ImportElementType(Type type) + { + ElementType etype; + if (!type_etype_mapping.TryGetValue(type, out etype)) + return ElementType.None; + + return etype; + } + + protected AssemblyNameReference ImportScope(SR.Assembly assembly) + { + return ImportReference(assembly.GetName()); + } + + public virtual AssemblyNameReference ImportReference(SR.AssemblyName name) + { + Mixin.CheckName(name); + + AssemblyNameReference reference; + if (TryGetAssemblyNameReference(name, out reference)) + return reference; + + reference = new AssemblyNameReference(name.Name, name.Version) + { + PublicKeyToken = name.GetPublicKeyToken(), + Culture = name.CultureInfo.Name, + HashAlgorithm = (AssemblyHashAlgorithm)name.HashAlgorithm, + }; + + module.AssemblyReferences.Add(reference); + return reference; + } + + bool TryGetAssemblyNameReference(SR.AssemblyName name, out AssemblyNameReference assembly_reference) + { + var references = module.AssemblyReferences; + + for (int i = 0; i < references.Count; i++) + { + var reference = references[i]; + if (name.FullName != reference.FullName) // TODO compare field by field + continue; + + assembly_reference = reference; + return true; + } + + assembly_reference = null; + return false; + } + + FieldReference ImportField(SR.FieldInfo field, ImportGenericContext context) + { + var declaring_type = ImportType(field.DeclaringType, context); + + if (IsGenericInstance(field.DeclaringType)) + field = ResolveFieldDefinition(field); + + context.Push(declaring_type); + try + { + return new FieldReference + { + Name = field.Name, + DeclaringType = declaring_type, + FieldType = ImportType(field.FieldType, context), + }; + } + finally + { + context.Pop(); + } + } + + static SR.FieldInfo ResolveFieldDefinition(SR.FieldInfo field) + { + return field.Module.ResolveField(field.MetadataToken); + } + + static SR.MethodBase ResolveMethodDefinition(SR.MethodBase method) + { + return method.Module.ResolveMethod(method.MetadataToken); + } + + MethodReference ImportMethod(SR.MethodBase method, ImportGenericContext context, ImportGenericKind import_kind) + { + if (IsMethodSpecification(method) || ImportOpenGenericMethod(method, import_kind)) + return ImportMethodSpecification(method, context); + + var declaring_type = ImportType(method.DeclaringType, context); + + if (IsGenericInstance(method.DeclaringType)) + method = ResolveMethodDefinition(method); + + var reference = new MethodReference + { + Name = method.Name, + HasThis = HasCallingConvention(method, SR.CallingConventions.HasThis), + ExplicitThis = HasCallingConvention(method, SR.CallingConventions.ExplicitThis), + DeclaringType = ImportType(method.DeclaringType, context, ImportGenericKind.Definition), + }; + + if (HasCallingConvention(method, SR.CallingConventions.VarArgs)) + reference.CallingConvention &= MethodCallingConvention.VarArg; + + if (method.IsGenericMethod) + ImportGenericParameters(reference, method.GetGenericArguments()); + + context.Push(reference); + try + { + var method_info = method as SR.MethodInfo; + reference.ReturnType = method_info != null + ? ImportType(method_info.ReturnType, context) + : ImportType(typeof(void), default(ImportGenericContext)); + + var parameters = method.GetParameters(); + var reference_parameters = reference.Parameters; + + for (int i = 0; i < parameters.Length; i++) + reference_parameters.Add( + new ParameterDefinition(ImportType(parameters[i].ParameterType, context))); + + reference.DeclaringType = declaring_type; + + return reference; + } + finally + { + context.Pop(); + } + } + + static void ImportGenericParameters(IGenericParameterProvider provider, Type[] arguments) + { + var provider_parameters = provider.GenericParameters; + + for (int i = 0; i < arguments.Length; i++) + provider_parameters.Add(new GenericParameter(arguments[i].Name, provider)); + } + + static bool IsMethodSpecification(SR.MethodBase method) + { + return method.IsGenericMethod && !method.IsGenericMethodDefinition; + } + + MethodReference ImportMethodSpecification(SR.MethodBase method, ImportGenericContext context) + { + var method_info = method as SR.MethodInfo; + if (method_info == null) + throw new InvalidOperationException(); + + var element_method = ImportMethod(method_info.GetGenericMethodDefinition(), context, ImportGenericKind.Definition); + var instance = new GenericInstanceMethod(element_method); + var arguments = method.GetGenericArguments(); + var instance_arguments = instance.GenericArguments; + + context.Push(element_method); + try + { + for (int i = 0; i < arguments.Length; i++) + instance_arguments.Add(ImportType(arguments[i], context)); + + return instance; + } + finally + { + context.Pop(); + } + } + + static bool HasCallingConvention(SR.MethodBase method, SR.CallingConventions conventions) + { + return (method.CallingConvention & conventions) != 0; + } + + public virtual TypeReference ImportReference(Type type, IGenericParameterProvider context) + { + Mixin.CheckType(type); + return ImportType( + type, + ImportGenericContext.For(context), + context != null ? ImportGenericKind.Open : ImportGenericKind.Definition); + } + + public virtual FieldReference ImportReference(SR.FieldInfo field, IGenericParameterProvider context) + { + Mixin.CheckField(field); + return ImportField(field, ImportGenericContext.For(context)); + } + + public virtual MethodReference ImportReference(SR.MethodBase method, IGenericParameterProvider context) + { + Mixin.CheckMethod(method); + return ImportMethod(method, + ImportGenericContext.For(context), + context != null ? ImportGenericKind.Open : ImportGenericKind.Definition); + } + } + + public class DefaultMetadataImporter : IMetadataImporter + { + + readonly protected ModuleDefinition module; + + public DefaultMetadataImporter(ModuleDefinition module) + { + Mixin.CheckModule(module); + + this.module = module; + } + + TypeReference ImportType(TypeReference type, ImportGenericContext context) + { + if (type.IsTypeSpecification()) + return ImportTypeSpecification(type, context); + + var reference = new TypeReference( + type.Namespace, + type.Name, + module, + ImportScope(type), + type.IsValueType); + + MetadataSystem.TryProcessPrimitiveTypeReference(reference); + + if (type.IsNested) + reference.DeclaringType = ImportType(type.DeclaringType, context); + + if (type.HasGenericParameters) + ImportGenericParameters(reference, type); + + return reference; + } + + protected virtual IMetadataScope ImportScope(TypeReference type) + { + return ImportScope(type.Scope); + } + + protected IMetadataScope ImportScope(IMetadataScope scope) + { + switch (scope.MetadataScopeType) + { + case MetadataScopeType.AssemblyNameReference: + return ImportReference((AssemblyNameReference)scope); + case MetadataScopeType.ModuleDefinition: + if (scope == module) return scope; + return ImportReference(((ModuleDefinition)scope).Assembly.Name); + case MetadataScopeType.ModuleReference: + throw new NotImplementedException(); + } + + throw new NotSupportedException(); + } + + public virtual AssemblyNameReference ImportReference(AssemblyNameReference name) + { + Mixin.CheckName(name); + + AssemblyNameReference reference; + if (module.TryGetAssemblyNameReference(name, out reference)) + return reference; + + reference = new AssemblyNameReference(name.Name, name.Version) + { + Culture = name.Culture, + HashAlgorithm = name.HashAlgorithm, + IsRetargetable = name.IsRetargetable, + IsWindowsRuntime = name.IsWindowsRuntime, + }; + + var pk_token = !name.PublicKeyToken.IsNullOrEmpty() + ? new byte[name.PublicKeyToken.Length] + : Empty.Array; + + if (pk_token.Length > 0) + Buffer.BlockCopy(name.PublicKeyToken, 0, pk_token, 0, pk_token.Length); + + reference.PublicKeyToken = pk_token; + + //Only add if not self. + if (CanAddAssemblyNameReference(module, reference)) + module.AssemblyReferences.Add(reference); + + return reference; + } + + private bool CanAddAssemblyNameReference(ModuleDefinition module, AssemblyNameReference nameRef) + { + return true; + //return (module.assembly.FullName != nameRef.FullName); + } + + static void ImportGenericParameters(IGenericParameterProvider imported, IGenericParameterProvider original) + { + var parameters = original.GenericParameters; + var imported_parameters = imported.GenericParameters; + + for (int i = 0; i < parameters.Count; i++) + imported_parameters.Add(new GenericParameter(parameters[i].Name, imported)); + } + + TypeReference ImportTypeSpecification(TypeReference type, ImportGenericContext context) + { + switch (type.etype) + { + case ElementType.SzArray: + var vector = (ArrayType)type; + return new ArrayType(ImportType(vector.ElementType, context)); + case ElementType.Ptr: + var pointer = (PointerType)type; + return new PointerType(ImportType(pointer.ElementType, context)); + case ElementType.ByRef: + var byref = (ByReferenceType)type; + return new ByReferenceType(ImportType(byref.ElementType, context)); + case ElementType.Pinned: + var pinned = (PinnedType)type; + return new PinnedType(ImportType(pinned.ElementType, context)); + case ElementType.Sentinel: + var sentinel = (SentinelType)type; + return new SentinelType(ImportType(sentinel.ElementType, context)); + case ElementType.FnPtr: + var fnptr = (FunctionPointerType)type; + var imported_fnptr = new FunctionPointerType() + { + HasThis = fnptr.HasThis, + ExplicitThis = fnptr.ExplicitThis, + CallingConvention = fnptr.CallingConvention, + ReturnType = ImportType(fnptr.ReturnType, context), + }; + + if (!fnptr.HasParameters) + return imported_fnptr; + + for (int i = 0; i < fnptr.Parameters.Count; i++) + imported_fnptr.Parameters.Add(new ParameterDefinition( + ImportType(fnptr.Parameters[i].ParameterType, context))); + + return imported_fnptr; + case ElementType.CModOpt: + var modopt = (OptionalModifierType)type; + return new OptionalModifierType( + ImportType(modopt.ModifierType, context), + ImportType(modopt.ElementType, context)); + case ElementType.CModReqD: + var modreq = (RequiredModifierType)type; + return new RequiredModifierType( + ImportType(modreq.ModifierType, context), + ImportType(modreq.ElementType, context)); + case ElementType.Array: + var array = (ArrayType)type; + var imported_array = new ArrayType(ImportType(array.ElementType, context)); + if (array.IsVector) + return imported_array; + + var dimensions = array.Dimensions; + var imported_dimensions = imported_array.Dimensions; + + imported_dimensions.Clear(); + + for (int i = 0; i < dimensions.Count; i++) + { + var dimension = dimensions[i]; + + imported_dimensions.Add(new ArrayDimension(dimension.LowerBound, dimension.UpperBound)); + } + + return imported_array; + case ElementType.GenericInst: + var instance = (GenericInstanceType)type; + var element_type = ImportType(instance.ElementType, context); + var arguments = instance.GenericArguments; + var imported_instance = new GenericInstanceType(element_type, arguments.Count); + var imported_arguments = imported_instance.GenericArguments; + + for (int i = 0; i < arguments.Count; i++) + imported_arguments.Add(ImportType(arguments[i], context)); + + return imported_instance; + case ElementType.Var: + var var_parameter = (GenericParameter)type; + if (var_parameter.DeclaringType == null) + throw new InvalidOperationException(); + return context.TypeParameter(var_parameter.DeclaringType.FullName, var_parameter.Position); + case ElementType.MVar: + var mvar_parameter = (GenericParameter)type; + if (mvar_parameter.DeclaringMethod == null) + throw new InvalidOperationException(); + return context.MethodParameter(context.NormalizeMethodName(mvar_parameter.DeclaringMethod), mvar_parameter.Position); + } + + throw new NotSupportedException(type.etype.ToString()); + } + + FieldReference ImportField(FieldReference field, ImportGenericContext context) + { + var declaring_type = ImportType(field.DeclaringType, context); + + context.Push(declaring_type); + try + { + return new FieldReference + { + Name = field.Name, + DeclaringType = declaring_type, + FieldType = ImportType(field.FieldType, context), + }; + } + finally + { + context.Pop(); + } + } + + MethodReference ImportMethod(MethodReference method, ImportGenericContext context) + { + if (method.IsGenericInstance) + return ImportMethodSpecification(method, context); + + var declaring_type = ImportType(method.DeclaringType, context); + + var reference = new MethodReference + { + Name = method.Name, + HasThis = method.HasThis, + ExplicitThis = method.ExplicitThis, + DeclaringType = declaring_type, + CallingConvention = method.CallingConvention, + }; + + if (method.HasGenericParameters) + ImportGenericParameters(reference, method); + + context.Push(reference); + try + { + reference.ReturnType = ImportType(method.ReturnType, context); + + if (!method.HasParameters) + return reference; + + var parameters = method.Parameters; + var reference_parameters = reference.parameters = new ParameterDefinitionCollection(reference, parameters.Count); + for (int i = 0; i < parameters.Count; i++) + reference_parameters.Add( + new ParameterDefinition(ImportType(parameters[i].ParameterType, context))); + + return reference; + } + finally + { + context.Pop(); + } + } + + MethodSpecification ImportMethodSpecification(MethodReference method, ImportGenericContext context) + { + if (!method.IsGenericInstance) + throw new NotSupportedException(); + + var instance = (GenericInstanceMethod)method; + var element_method = ImportMethod(instance.ElementMethod, context); + var imported_instance = new GenericInstanceMethod(element_method); + + var arguments = instance.GenericArguments; + var imported_arguments = imported_instance.GenericArguments; + + for (int i = 0; i < arguments.Count; i++) + imported_arguments.Add(ImportType(arguments[i], context)); + + return imported_instance; + } + + public virtual TypeReference ImportReference(TypeReference type, IGenericParameterProvider context) + { + Mixin.CheckType(type); + return ImportType(type, ImportGenericContext.For(context)); + } + + public virtual FieldReference ImportReference(FieldReference field, IGenericParameterProvider context) + { + Mixin.CheckField(field); + return ImportField(field, ImportGenericContext.For(context)); + } + + public virtual MethodReference ImportReference(MethodReference method, IGenericParameterProvider context) + { + Mixin.CheckMethod(method); + return ImportMethod(method, ImportGenericContext.For(context)); + } + } + + static partial class Mixin + { + + public static void CheckModule(ModuleDefinition module) + { + if (module == null) + throw new ArgumentNullException(Argument.module.ToString()); + } + + public static bool TryGetAssemblyNameReference(this ModuleDefinition module, AssemblyNameReference name_reference, out AssemblyNameReference assembly_reference) + { + var references = module.AssemblyReferences; + + for (int i = 0; i < references.Count; i++) + { + var reference = references[i]; + if (!Equals(name_reference, reference)) + continue; + + assembly_reference = reference; + return true; + } + + assembly_reference = null; + return false; + } + + static bool Equals(byte[] a, byte[] b) + { + if (ReferenceEquals(a, b)) + return true; + if (a == null) + return false; + if (a.Length != b.Length) + return false; + for (int i = 0; i < a.Length; i++) + if (a[i] != b[i]) + return false; + return true; + } + + static bool Equals(T a, T b) where T : class, IEquatable + { + if (ReferenceEquals(a, b)) + return true; + if (a == null) + return false; + return a.Equals(b); + } + + static bool Equals(AssemblyNameReference a, AssemblyNameReference b) + { + if (ReferenceEquals(a, b)) + return true; + if (a.Name != b.Name) + return false; + if (!Equals(a.Version, b.Version)) + return false; + if (a.Culture != b.Culture) + return false; + if (!Equals(a.PublicKeyToken, b.PublicKeyToken)) + return false; + return true; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Import.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Import.cs.meta new file mode 100644 index 0000000..f898f90 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Import.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fa57d261f1a90c746abaef3d59b4c655 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/LinkedResource.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/LinkedResource.cs new file mode 100644 index 0000000..392ffdb --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/LinkedResource.cs @@ -0,0 +1,42 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil { + + public sealed class LinkedResource : Resource { + + internal byte [] hash; + string file; + + public byte [] Hash { + get { return hash; } + } + + public string File { + get { return file; } + set { file = value; } + } + + public override ResourceType ResourceType { + get { return ResourceType.Linked; } + } + + public LinkedResource (string name, ManifestResourceAttributes flags) + : base (name, flags) + { + } + + public LinkedResource (string name, ManifestResourceAttributes flags, string file) + : base (name, flags) + { + this.file = file; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/LinkedResource.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/LinkedResource.cs.meta new file mode 100644 index 0000000..531db4c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/LinkedResource.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 18c45c1a0891fdc40a07d309b564ec6d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ManifestResourceAttributes.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ManifestResourceAttributes.cs new file mode 100644 index 0000000..693004c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ManifestResourceAttributes.cs @@ -0,0 +1,21 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil { + + [Flags] + public enum ManifestResourceAttributes : uint { + VisibilityMask = 0x0007, + Public = 0x0001, // The resource is exported from the Assembly + Private = 0x0002 // The resource is private to the Assembly + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ManifestResourceAttributes.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ManifestResourceAttributes.cs.meta new file mode 100644 index 0000000..a88aad4 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ManifestResourceAttributes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 136b98b695702f048857ea31d91193fc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MarshalInfo.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MarshalInfo.cs new file mode 100644 index 0000000..e93d1ef --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MarshalInfo.cs @@ -0,0 +1,153 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil { + + public class MarshalInfo { + + internal NativeType native; + + public NativeType NativeType { + get { return native; } + set { native = value; } + } + + public MarshalInfo (NativeType native) + { + this.native = native; + } + } + + public sealed class ArrayMarshalInfo : MarshalInfo { + + internal NativeType element_type; + internal int size_parameter_index; + internal int size; + internal int size_parameter_multiplier; + + public NativeType ElementType { + get { return element_type; } + set { element_type = value; } + } + + public int SizeParameterIndex { + get { return size_parameter_index; } + set { size_parameter_index = value; } + } + + public int Size { + get { return size; } + set { size = value; } + } + + public int SizeParameterMultiplier { + get { return size_parameter_multiplier; } + set { size_parameter_multiplier = value; } + } + + public ArrayMarshalInfo () + : base (NativeType.Array) + { + element_type = NativeType.None; + size_parameter_index = -1; + size = -1; + size_parameter_multiplier = -1; + } + } + + public sealed class CustomMarshalInfo : MarshalInfo { + + internal Guid guid; + internal string unmanaged_type; + internal TypeReference managed_type; + internal string cookie; + + public Guid Guid { + get { return guid; } + set { guid = value; } + } + + public string UnmanagedType { + get { return unmanaged_type; } + set { unmanaged_type = value; } + } + + public TypeReference ManagedType { + get { return managed_type; } + set { managed_type = value; } + } + + public string Cookie { + get { return cookie; } + set { cookie = value; } + } + + public CustomMarshalInfo () + : base (NativeType.CustomMarshaler) + { + } + } + + public sealed class SafeArrayMarshalInfo : MarshalInfo { + + internal VariantType element_type; + + public VariantType ElementType { + get { return element_type; } + set { element_type = value; } + } + + public SafeArrayMarshalInfo () + : base (NativeType.SafeArray) + { + element_type = VariantType.None; + } + } + + public sealed class FixedArrayMarshalInfo : MarshalInfo { + + internal NativeType element_type; + internal int size; + + public NativeType ElementType { + get { return element_type; } + set { element_type = value; } + } + + public int Size { + get { return size; } + set { size = value; } + } + + public FixedArrayMarshalInfo () + : base (NativeType.FixedArray) + { + element_type = NativeType.None; + } + } + + public sealed class FixedSysStringMarshalInfo : MarshalInfo { + + internal int size; + + public int Size { + get { return size; } + set { size = value; } + } + + public FixedSysStringMarshalInfo () + : base (NativeType.FixedSysString) + { + size = -1; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MarshalInfo.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MarshalInfo.cs.meta new file mode 100644 index 0000000..1a110e3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MarshalInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 081d03dc18ced1648ae4d9f2eefd3370 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MemberDefinitionCollection.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MemberDefinitionCollection.cs new file mode 100644 index 0000000..172d349 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MemberDefinitionCollection.cs @@ -0,0 +1,73 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System; + +namespace MonoFN.Cecil { + + sealed class MemberDefinitionCollection : Collection where T : IMemberDefinition { + + TypeDefinition container; + + internal MemberDefinitionCollection (TypeDefinition container) + { + this.container = container; + } + + internal MemberDefinitionCollection (TypeDefinition container, int capacity) + : base (capacity) + { + this.container = container; + } + + protected override void OnAdd (T item, int index) + { + Attach (item); + } + + protected sealed override void OnSet (T item, int index) + { + Attach (item); + } + + protected sealed override void OnInsert (T item, int index) + { + Attach (item); + } + + protected sealed override void OnRemove (T item, int index) + { + Detach (item); + } + + protected sealed override void OnClear () + { + foreach (var definition in this) + Detach (definition); + } + + void Attach (T element) + { + if (element.DeclaringType == container) + return; + + if (element.DeclaringType != null) + throw new ArgumentException ("Member already attached"); + + element.DeclaringType = this.container; + } + + static void Detach (T element) + { + element.DeclaringType = null; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MemberDefinitionCollection.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MemberDefinitionCollection.cs.meta new file mode 100644 index 0000000..6684de2 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MemberDefinitionCollection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e5caa999f42a585459c62b7b65eefdd8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MemberReference.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MemberReference.cs new file mode 100644 index 0000000..4f75843 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MemberReference.cs @@ -0,0 +1,102 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil { + + public abstract class MemberReference : IMetadataTokenProvider { + + string name; + TypeReference declaring_type; + + internal MetadataToken token; + internal object projection; + + public virtual string Name { + get { return name; } + set { + if (IsWindowsRuntimeProjection && value != name) + throw new InvalidOperationException (); + + name = value; + } + } + + public abstract string FullName { + get; + } + + public virtual TypeReference DeclaringType { + get { return declaring_type; } + set { declaring_type = value; } + } + + public MetadataToken MetadataToken { + get { return token; } + set { token = value; } + } + + public bool IsWindowsRuntimeProjection { + get { return projection != null; } + } + + internal bool HasImage { + get { + var module = Module; + if (module == null) + return false; + + return module.HasImage; + } + } + + public virtual ModuleDefinition Module { + get { return declaring_type != null ? declaring_type.Module : null; } + } + + public virtual bool IsDefinition { + get { return false; } + } + + public virtual bool ContainsGenericParameter { + get { return declaring_type != null && declaring_type.ContainsGenericParameter; } + } + + internal MemberReference () + { + } + + internal MemberReference (string name) + { + this.name = name ?? string.Empty; + } + + internal string MemberFullName () + { + if (declaring_type == null) + return name; + + return declaring_type.FullName + "::" + name; + } + + public IMemberDefinition Resolve () + { + return ResolveDefinition (); + } + + protected abstract IMemberDefinition ResolveDefinition (); + + public override string ToString () + { + return FullName; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MemberReference.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MemberReference.cs.meta new file mode 100644 index 0000000..c3d8f54 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MemberReference.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2dcf551ad731c204fa148ec1ee0ce881 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MetadataResolver.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MetadataResolver.cs new file mode 100644 index 0000000..ce212e4 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MetadataResolver.cs @@ -0,0 +1,391 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System; + +namespace MonoFN.Cecil { + + public interface IAssemblyResolver : IDisposable { + AssemblyDefinition Resolve (AssemblyNameReference name); + AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters); + } + + public interface IMetadataResolver { + TypeDefinition Resolve (TypeReference type); + FieldDefinition Resolve (FieldReference field); + MethodDefinition Resolve (MethodReference method); + } + +#if !NET_CORE + [Serializable] +#endif + public sealed class ResolutionException : Exception { + + readonly MemberReference member; + + public MemberReference Member { + get { return member; } + } + + public IMetadataScope Scope { + get { + var type = member as TypeReference; + if (type != null) + return type.Scope; + + var declaring_type = member.DeclaringType; + if (declaring_type != null) + return declaring_type.Scope; + + throw new NotSupportedException (); + } + } + + public ResolutionException (MemberReference member) + : base ("Failed to resolve " + member.FullName) + { + if (member == null) + throw new ArgumentNullException ("member"); + + this.member = member; + } + + public ResolutionException (MemberReference member, Exception innerException) + : base ("Failed to resolve " + member.FullName, innerException) + { + if (member == null) + throw new ArgumentNullException ("member"); + + this.member = member; + } + +#if !NET_CORE + ResolutionException ( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) + : base (info, context) + { + } +#endif + } + + public class MetadataResolver : IMetadataResolver { + + readonly IAssemblyResolver assembly_resolver; + + public IAssemblyResolver AssemblyResolver { + get { return assembly_resolver; } + } + + public MetadataResolver (IAssemblyResolver assemblyResolver) + { + if (assemblyResolver == null) + throw new ArgumentNullException ("assemblyResolver"); + + assembly_resolver = assemblyResolver; + } + + public virtual TypeDefinition Resolve (TypeReference type) + { + Mixin.CheckType (type); + + type = type.GetElementType (); + + var scope = type.Scope; + + if (scope == null) + return null; + + switch (scope.MetadataScopeType) { + case MetadataScopeType.AssemblyNameReference: + var assembly = assembly_resolver.Resolve ((AssemblyNameReference)scope); + if (assembly == null) + return null; + + return GetType (assembly.MainModule, type); + case MetadataScopeType.ModuleDefinition: + return GetType ((ModuleDefinition)scope, type); + case MetadataScopeType.ModuleReference: + if (type.Module.Assembly == null) + return null; + + var modules = type.Module.Assembly.Modules; + var module_ref = (ModuleReference)scope; + for (int i = 0; i < modules.Count; i++) { + var netmodule = modules [i]; + if (netmodule.Name == module_ref.Name) + return GetType (netmodule, type); + } + break; + } + + throw new NotSupportedException (); + } + + static TypeDefinition GetType (ModuleDefinition module, TypeReference reference) + { + var type = GetTypeDefinition (module, reference); + if (type != null) + return type; + + if (!module.HasExportedTypes) + return null; + + var exported_types = module.ExportedTypes; + + for (int i = 0; i < exported_types.Count; i++) { + var exported_type = exported_types [i]; + if (exported_type.Name != reference.Name) + continue; + + if (exported_type.Namespace != reference.Namespace) + continue; + + return exported_type.Resolve (); + } + + return null; + } + + static TypeDefinition GetTypeDefinition (ModuleDefinition module, TypeReference type) + { + if (!type.IsNested) + return module.GetType (type.Namespace, type.Name); + + var declaring_type = type.DeclaringType.Resolve (); + if (declaring_type == null) + return null; + + return declaring_type.GetNestedType (type.TypeFullName ()); + } + + public virtual FieldDefinition Resolve (FieldReference field) + { + Mixin.CheckField (field); + + var type = Resolve (field.DeclaringType); + if (type == null) + return null; + + if (!type.HasFields) + return null; + + return GetField (type, field); + } + + FieldDefinition GetField (TypeDefinition type, FieldReference reference) + { + while (type != null) { + var field = GetField (type.Fields, reference); + if (field != null) + return field; + + if (type.BaseType == null) + return null; + + type = Resolve (type.BaseType); + } + + return null; + } + + static FieldDefinition GetField (Collection fields, FieldReference reference) + { + for (int i = 0; i < fields.Count; i++) { + var field = fields [i]; + + if (field.Name != reference.Name) + continue; + + if (!AreSame (field.FieldType, reference.FieldType)) + continue; + + return field; + } + + return null; + } + + public virtual MethodDefinition Resolve (MethodReference method) + { + Mixin.CheckMethod (method); + + var type = Resolve (method.DeclaringType); + if (type == null) + return null; + + method = method.GetElementMethod (); + + if (!type.HasMethods) + return null; + + return GetMethod (type, method); + } + + MethodDefinition GetMethod (TypeDefinition type, MethodReference reference) + { + while (type != null) { + var method = GetMethod (type.Methods, reference); + if (method != null) + return method; + + if (type.BaseType == null) + return null; + + type = Resolve (type.BaseType); + } + + return null; + } + + public static MethodDefinition GetMethod (Collection methods, MethodReference reference) + { + for (int i = 0; i < methods.Count; i++) { + var method = methods [i]; + + if (method.Name != reference.Name) + continue; + + if (method.HasGenericParameters != reference.HasGenericParameters) + continue; + + if (method.HasGenericParameters && method.GenericParameters.Count != reference.GenericParameters.Count) + continue; + + if (!AreSame (method.ReturnType, reference.ReturnType)) + continue; + + if (method.IsVarArg () != reference.IsVarArg ()) + continue; + + if (method.IsVarArg () && IsVarArgCallTo (method, reference)) + return method; + + if (method.HasParameters != reference.HasParameters) + continue; + + if (!method.HasParameters && !reference.HasParameters) + return method; + + if (!AreSame (method.Parameters, reference.Parameters)) + continue; + + return method; + } + + return null; + } + + static bool AreSame (Collection a, Collection b) + { + var count = a.Count; + + if (count != b.Count) + return false; + + if (count == 0) + return true; + + for (int i = 0; i < count; i++) + if (!AreSame (a [i].ParameterType, b [i].ParameterType)) + return false; + + return true; + } + + static bool IsVarArgCallTo (MethodDefinition method, MethodReference reference) + { + if (method.Parameters.Count >= reference.Parameters.Count) + return false; + + if (reference.GetSentinelPosition () != method.Parameters.Count) + return false; + + for (int i = 0; i < method.Parameters.Count; i++) + if (!AreSame (method.Parameters [i].ParameterType, reference.Parameters [i].ParameterType)) + return false; + + return true; + } + + static bool AreSame (TypeSpecification a, TypeSpecification b) + { + if (!AreSame (a.ElementType, b.ElementType)) + return false; + + if (a.IsGenericInstance) + return AreSame ((GenericInstanceType)a, (GenericInstanceType)b); + + if (a.IsRequiredModifier || a.IsOptionalModifier) + return AreSame ((IModifierType)a, (IModifierType)b); + + if (a.IsArray) + return AreSame ((ArrayType)a, (ArrayType)b); + + return true; + } + + static bool AreSame (ArrayType a, ArrayType b) + { + if (a.Rank != b.Rank) + return false; + + // TODO: dimensions + + return true; + } + + static bool AreSame (IModifierType a, IModifierType b) + { + return AreSame (a.ModifierType, b.ModifierType); + } + + static bool AreSame (GenericInstanceType a, GenericInstanceType b) + { + if (a.GenericArguments.Count != b.GenericArguments.Count) + return false; + + for (int i = 0; i < a.GenericArguments.Count; i++) + if (!AreSame (a.GenericArguments [i], b.GenericArguments [i])) + return false; + + return true; + } + + static bool AreSame (GenericParameter a, GenericParameter b) + { + return a.Position == b.Position; + } + + static bool AreSame (TypeReference a, TypeReference b) + { + if (ReferenceEquals (a, b)) + return true; + + if (a == null || b == null) + return false; + + if (a.etype != b.etype) + return false; + + if (a.IsGenericParameter) + return AreSame ((GenericParameter)a, (GenericParameter)b); + + if (a.IsTypeSpecification ()) + return AreSame ((TypeSpecification)a, (TypeSpecification)b); + + if (a.Name != b.Name || a.Namespace != b.Namespace) + return false; + + return AreSame (a.DeclaringType, b.DeclaringType); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MetadataResolver.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MetadataResolver.cs.meta new file mode 100644 index 0000000..9a42f6b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MetadataResolver.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fdd2c926d773ffd4692e2d042135fd2b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MetadataSystem.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MetadataSystem.cs new file mode 100644 index 0000000..00ef319 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MetadataSystem.cs @@ -0,0 +1,431 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.Cil; +using MonoFN.Cecil.Metadata; +using MonoFN.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Threading; + +namespace MonoFN.Cecil { + + struct Range { + public uint Start; + public uint Length; + + public Range (uint index, uint length) + { + this.Start = index; + this.Length = length; + } + } + + sealed class MetadataSystem { + + internal AssemblyNameReference [] AssemblyReferences; + internal ModuleReference [] ModuleReferences; + + internal TypeDefinition [] Types; + internal TypeReference [] TypeReferences; + + internal FieldDefinition [] Fields; + internal MethodDefinition [] Methods; + internal MemberReference [] MemberReferences; + + internal Dictionary> NestedTypes; + internal Dictionary ReverseNestedTypes; + internal Dictionary>> Interfaces; + internal Dictionary> ClassLayouts; + internal Dictionary FieldLayouts; + internal Dictionary FieldRVAs; + internal Dictionary FieldMarshals; + internal Dictionary> Constants; + internal Dictionary> Overrides; + internal Dictionary CustomAttributes; + internal Dictionary SecurityDeclarations; + internal Dictionary Events; + internal Dictionary Properties; + internal Dictionary> Semantics; + internal Dictionary> PInvokes; + internal Dictionary GenericParameters; + internal Dictionary>> GenericConstraints; + + internal Document [] Documents; + internal Dictionary>> LocalScopes; + internal ImportDebugInformation [] ImportScopes; + internal Dictionary StateMachineMethods; + internal Dictionary []> CustomDebugInformations; + + static Dictionary> primitive_value_types; + + static void InitializePrimitives () + { + var types = new Dictionary> (18, StringComparer.Ordinal) { + { "Void", new Row (ElementType.Void, false) }, + { "Boolean", new Row (ElementType.Boolean, true) }, + { "Char", new Row (ElementType.Char, true) }, + { "SByte", new Row (ElementType.I1, true) }, + { "Byte", new Row (ElementType.U1, true) }, + { "Int16", new Row (ElementType.I2, true) }, + { "UInt16", new Row (ElementType.U2, true) }, + { "Int32", new Row (ElementType.I4, true) }, + { "UInt32", new Row (ElementType.U4, true) }, + { "Int64", new Row (ElementType.I8, true) }, + { "UInt64", new Row (ElementType.U8, true) }, + { "Single", new Row (ElementType.R4, true) }, + { "Double", new Row (ElementType.R8, true) }, + { "String", new Row (ElementType.String, false) }, + { "TypedReference", new Row (ElementType.TypedByRef, false) }, + { "IntPtr", new Row (ElementType.I, true) }, + { "UIntPtr", new Row (ElementType.U, true) }, + { "Object", new Row (ElementType.Object, false) }, + }; + + Interlocked.CompareExchange (ref primitive_value_types, types, null); + } + + public static void TryProcessPrimitiveTypeReference (TypeReference type) + { + if (type.Namespace != "System") + return; + + var scope = type.scope; + if (scope == null || scope.MetadataScopeType != MetadataScopeType.AssemblyNameReference) + return; + + Row primitive_data; + if (!TryGetPrimitiveData (type, out primitive_data)) + return; + + type.etype = primitive_data.Col1; + type.IsValueType = primitive_data.Col2; + } + + public static bool TryGetPrimitiveElementType (TypeDefinition type, out ElementType etype) + { + etype = ElementType.None; + + if (type.Namespace != "System") + return false; + + Row primitive_data; + if (TryGetPrimitiveData (type, out primitive_data)) { + etype = primitive_data.Col1; + return true; + } + + return false; + } + + static bool TryGetPrimitiveData (TypeReference type, out Row primitive_data) + { + if (primitive_value_types == null) + InitializePrimitives (); + + return primitive_value_types.TryGetValue (type.Name, out primitive_data); + } + + public void Clear () + { + if (NestedTypes != null) NestedTypes = new Dictionary> (capacity: 0); + if (ReverseNestedTypes != null) ReverseNestedTypes = new Dictionary (capacity: 0); + if (Interfaces != null) Interfaces = new Dictionary>> (capacity: 0); + if (ClassLayouts != null) ClassLayouts = new Dictionary> (capacity: 0); + if (FieldLayouts != null) FieldLayouts = new Dictionary (capacity: 0); + if (FieldRVAs != null) FieldRVAs = new Dictionary (capacity: 0); + if (FieldMarshals != null) FieldMarshals = new Dictionary (capacity: 0); + if (Constants != null) Constants = new Dictionary> (capacity: 0); + if (Overrides != null) Overrides = new Dictionary> (capacity: 0); + if (CustomAttributes != null) CustomAttributes = new Dictionary (capacity: 0); + if (SecurityDeclarations != null) SecurityDeclarations = new Dictionary (capacity: 0); + if (Events != null) Events = new Dictionary (capacity: 0); + if (Properties != null) Properties = new Dictionary (capacity: 0); + if (Semantics != null) Semantics = new Dictionary> (capacity: 0); + if (PInvokes != null) PInvokes = new Dictionary> (capacity: 0); + if (GenericParameters != null) GenericParameters = new Dictionary (capacity: 0); + if (GenericConstraints != null) GenericConstraints = new Dictionary>> (capacity: 0); + + Documents = Empty.Array; + ImportScopes = Empty.Array; + if (LocalScopes != null) LocalScopes = new Dictionary>> (capacity: 0); + if (StateMachineMethods != null) StateMachineMethods = new Dictionary (capacity: 0); + } + + public AssemblyNameReference GetAssemblyNameReference (uint rid) + { + if (rid < 1 || rid > AssemblyReferences.Length) + return null; + + return AssemblyReferences [rid - 1]; + } + + public TypeDefinition GetTypeDefinition (uint rid) + { + if (rid < 1 || rid > Types.Length) + return null; + + return Types [rid - 1]; + } + + public void AddTypeDefinition (TypeDefinition type) + { + Types [type.token.RID - 1] = type; + } + + public TypeReference GetTypeReference (uint rid) + { + if (rid < 1 || rid > TypeReferences.Length) + return null; + + return TypeReferences [rid - 1]; + } + + public void AddTypeReference (TypeReference type) + { + TypeReferences [type.token.RID - 1] = type; + } + + public FieldDefinition GetFieldDefinition (uint rid) + { + if (rid < 1 || rid > Fields.Length) + return null; + + return Fields [rid - 1]; + } + + public void AddFieldDefinition (FieldDefinition field) + { + Fields [field.token.RID - 1] = field; + } + + public MethodDefinition GetMethodDefinition (uint rid) + { + if (rid < 1 || rid > Methods.Length) + return null; + + return Methods [rid - 1]; + } + + public void AddMethodDefinition (MethodDefinition method) + { + Methods [method.token.RID - 1] = method; + } + + public MemberReference GetMemberReference (uint rid) + { + if (rid < 1 || rid > MemberReferences.Length) + return null; + + return MemberReferences [rid - 1]; + } + + public void AddMemberReference (MemberReference member) + { + MemberReferences [member.token.RID - 1] = member; + } + + public bool TryGetNestedTypeMapping (TypeDefinition type, out Collection mapping) + { + return NestedTypes.TryGetValue (type.token.RID, out mapping); + } + + public void SetNestedTypeMapping (uint type_rid, Collection mapping) + { + NestedTypes [type_rid] = mapping; + } + + public void RemoveNestedTypeMapping (TypeDefinition type) + { + NestedTypes.Remove (type.token.RID); + } + + public bool TryGetReverseNestedTypeMapping (TypeDefinition type, out uint declaring) + { + return ReverseNestedTypes.TryGetValue (type.token.RID, out declaring); + } + + public void SetReverseNestedTypeMapping (uint nested, uint declaring) + { + ReverseNestedTypes [nested] = declaring; + } + + public void RemoveReverseNestedTypeMapping (TypeDefinition type) + { + ReverseNestedTypes.Remove (type.token.RID); + } + + public bool TryGetInterfaceMapping (TypeDefinition type, out Collection> mapping) + { + return Interfaces.TryGetValue (type.token.RID, out mapping); + } + + public void SetInterfaceMapping (uint type_rid, Collection> mapping) + { + Interfaces [type_rid] = mapping; + } + + public void RemoveInterfaceMapping (TypeDefinition type) + { + Interfaces.Remove (type.token.RID); + } + + public void AddPropertiesRange (uint type_rid, Range range) + { + Properties.Add (type_rid, range); + } + + public bool TryGetPropertiesRange (TypeDefinition type, out Range range) + { + return Properties.TryGetValue (type.token.RID, out range); + } + + public void RemovePropertiesRange (TypeDefinition type) + { + Properties.Remove (type.token.RID); + } + + public void AddEventsRange (uint type_rid, Range range) + { + Events.Add (type_rid, range); + } + + public bool TryGetEventsRange (TypeDefinition type, out Range range) + { + return Events.TryGetValue (type.token.RID, out range); + } + + public void RemoveEventsRange (TypeDefinition type) + { + Events.Remove (type.token.RID); + } + + public bool TryGetGenericParameterRanges (IGenericParameterProvider owner, out Range [] ranges) + { + return GenericParameters.TryGetValue (owner.MetadataToken, out ranges); + } + + public void RemoveGenericParameterRange (IGenericParameterProvider owner) + { + GenericParameters.Remove (owner.MetadataToken); + } + + public bool TryGetCustomAttributeRanges (ICustomAttributeProvider owner, out Range [] ranges) + { + return CustomAttributes.TryGetValue (owner.MetadataToken, out ranges); + } + + public void RemoveCustomAttributeRange (ICustomAttributeProvider owner) + { + CustomAttributes.Remove (owner.MetadataToken); + } + + public bool TryGetSecurityDeclarationRanges (ISecurityDeclarationProvider owner, out Range [] ranges) + { + return SecurityDeclarations.TryGetValue (owner.MetadataToken, out ranges); + } + + public void RemoveSecurityDeclarationRange (ISecurityDeclarationProvider owner) + { + SecurityDeclarations.Remove (owner.MetadataToken); + } + + public bool TryGetGenericConstraintMapping (GenericParameter generic_parameter, out Collection> mapping) + { + return GenericConstraints.TryGetValue (generic_parameter.token.RID, out mapping); + } + + public void SetGenericConstraintMapping (uint gp_rid, Collection> mapping) + { + GenericConstraints [gp_rid] = mapping; + } + + public void RemoveGenericConstraintMapping (GenericParameter generic_parameter) + { + GenericConstraints.Remove (generic_parameter.token.RID); + } + + public bool TryGetOverrideMapping (MethodDefinition method, out Collection mapping) + { + return Overrides.TryGetValue (method.token.RID, out mapping); + } + + public void SetOverrideMapping (uint rid, Collection mapping) + { + Overrides [rid] = mapping; + } + + public void RemoveOverrideMapping (MethodDefinition method) + { + Overrides.Remove (method.token.RID); + } + + public Document GetDocument (uint rid) + { + if (rid < 1 || rid > Documents.Length) + return null; + + return Documents [rid - 1]; + } + + public bool TryGetLocalScopes (MethodDefinition method, out Collection> scopes) + { + return LocalScopes.TryGetValue (method.MetadataToken.RID, out scopes); + } + + public void SetLocalScopes (uint method_rid, Collection> records) + { + LocalScopes [method_rid] = records; + } + + public ImportDebugInformation GetImportScope (uint rid) + { + if (rid < 1 || rid > ImportScopes.Length) + return null; + + return ImportScopes [rid - 1]; + } + + public bool TryGetStateMachineKickOffMethod (MethodDefinition method, out uint rid) + { + return StateMachineMethods.TryGetValue (method.MetadataToken.RID, out rid); + } + + public TypeDefinition GetFieldDeclaringType (uint field_rid) + { + return BinaryRangeSearch (Types, field_rid, true); + } + + public TypeDefinition GetMethodDeclaringType (uint method_rid) + { + return BinaryRangeSearch (Types, method_rid, false); + } + + static TypeDefinition BinaryRangeSearch (TypeDefinition [] types, uint rid, bool field) + { + int min = 0; + int max = types.Length - 1; + while (min <= max) { + int mid = min + ((max - min) / 2); + var type = types [mid]; + var range = field ? type.fields_range : type.methods_range; + + if (rid < range.Start) + max = mid - 1; + else if (rid >= range.Start + range.Length) + min = mid + 1; + else + return type; + } + + return null; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MetadataSystem.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MetadataSystem.cs.meta new file mode 100644 index 0000000..46912ce --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MetadataSystem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6ea39c5122499d14fbca126d3ed39890 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodAttributes.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodAttributes.cs new file mode 100644 index 0000000..043e77a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodAttributes.cs @@ -0,0 +1,48 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil { + + [Flags] + public enum MethodAttributes : ushort { + MemberAccessMask = 0x0007, + CompilerControlled = 0x0000, // Member not referenceable + Private = 0x0001, // Accessible only by the parent type + FamANDAssem = 0x0002, // Accessible by sub-types only in this Assembly + Assembly = 0x0003, // Accessibly by anyone in the Assembly + Family = 0x0004, // Accessible only by type and sub-types + FamORAssem = 0x0005, // Accessibly by sub-types anywhere, plus anyone in assembly + Public = 0x0006, // Accessibly by anyone who has visibility to this scope + + Static = 0x0010, // Defined on type, else per instance + Final = 0x0020, // Method may not be overridden + Virtual = 0x0040, // Method is virtual + HideBySig = 0x0080, // Method hides by name+sig, else just by name + + VtableLayoutMask = 0x0100, // Use this mask to retrieve vtable attributes + ReuseSlot = 0x0000, // Method reuses existing slot in vtable + NewSlot = 0x0100, // Method always gets a new slot in the vtable + + CheckAccessOnOverride = 0x0200, // Method can only be overriden if also accessible + Abstract = 0x0400, // Method does not provide an implementation + SpecialName = 0x0800, // Method is special + + // Interop Attributes + PInvokeImpl = 0x2000, // Implementation is forwarded through PInvoke + UnmanagedExport = 0x0008, // Reserved: shall be zero for conforming implementations + + // Additional flags + RTSpecialName = 0x1000, // CLI provides 'special' behavior, depending upon the name of the method + HasSecurity = 0x4000, // Method has security associate with it + RequireSecObject = 0x8000 // Method calls another method containing security code + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodAttributes.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodAttributes.cs.meta new file mode 100644 index 0000000..6060bf0 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodAttributes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 84996fdfc826be145836c454e3781c51 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodCallingConvention.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodCallingConvention.cs new file mode 100644 index 0000000..2c219ba --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodCallingConvention.cs @@ -0,0 +1,22 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil { + + public enum MethodCallingConvention : byte { + Default = 0x0, + C = 0x1, + StdCall = 0x2, + ThisCall = 0x3, + FastCall = 0x4, + VarArg = 0x5, + Generic = 0x10, + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodCallingConvention.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodCallingConvention.cs.meta new file mode 100644 index 0000000..0897aab --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodCallingConvention.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1d279830051a3cf4a9a8198756171875 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodDefinition.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodDefinition.cs new file mode 100644 index 0000000..164c0fb --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodDefinition.cs @@ -0,0 +1,558 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.Cil; +using MonoFN.Collections.Generic; +using System; +using System.Threading; +using RVA = System.UInt32; + +namespace MonoFN.Cecil { + + public sealed class MethodDefinition : MethodReference, IMemberDefinition, ISecurityDeclarationProvider, ICustomDebugInformationProvider { + + ushort attributes; + ushort impl_attributes; + internal volatile bool sem_attrs_ready; + internal MethodSemanticsAttributes sem_attrs; + Collection custom_attributes; + Collection security_declarations; + + internal RVA rva; + internal PInvokeInfo pinvoke; + Collection overrides; + + internal MethodBody body; + internal MethodDebugInformation debug_info; + internal Collection custom_infos; + + public override string Name { + get { return base.Name; } + set { + if (IsWindowsRuntimeProjection && value != base.Name) + throw new InvalidOperationException (); + + base.Name = value; + } + } + + public MethodAttributes Attributes { + get { return (MethodAttributes)attributes; } + set { + if (IsWindowsRuntimeProjection && (ushort)value != attributes) + throw new InvalidOperationException (); + + attributes = (ushort)value; + } + } + + public MethodImplAttributes ImplAttributes { + get { return (MethodImplAttributes)impl_attributes; } + set { + if (IsWindowsRuntimeProjection && (ushort)value != impl_attributes) + throw new InvalidOperationException (); + + impl_attributes = (ushort)value; + } + } + + public MethodSemanticsAttributes SemanticsAttributes { + get { + if (sem_attrs_ready) + return sem_attrs; + + if (HasImage) { + ReadSemantics (); + return sem_attrs; + } + + sem_attrs = MethodSemanticsAttributes.None; + sem_attrs_ready = true; + return sem_attrs; + } + set { sem_attrs = value; } + } + + internal MethodDefinitionProjection WindowsRuntimeProjection { + get { return (MethodDefinitionProjection)projection; } + set { projection = value; } + } + + internal void ReadSemantics () + { + if (sem_attrs_ready) + return; + + var module = this.Module; + if (module == null) + return; + + if (!module.HasImage) + return; + + lock (module.SyncRoot) { + if (sem_attrs_ready) + return; + + module.Read (this, (method, reader) => reader.ReadAllSemantics (method)); + } + } + + public bool HasSecurityDeclarations { + get { + if (security_declarations != null) + return security_declarations.Count > 0; + + return this.GetHasSecurityDeclarations (Module); + } + } + + public Collection SecurityDeclarations { + get { return security_declarations ?? (this.GetSecurityDeclarations (ref security_declarations, Module)); } + } + + public bool HasCustomAttributes { + get { + if (custom_attributes != null) + return custom_attributes.Count > 0; + + return this.GetHasCustomAttributes (Module); + } + } + + public Collection CustomAttributes { + get { return custom_attributes ?? (this.GetCustomAttributes (ref custom_attributes, Module)); } + } + + public int RVA { + get { return (int)rva; } + } + + public bool HasBody { + get { + return (attributes & (ushort)MethodAttributes.Abstract) == 0 && + (attributes & (ushort)MethodAttributes.PInvokeImpl) == 0 && + (impl_attributes & (ushort)MethodImplAttributes.InternalCall) == 0 && + (impl_attributes & (ushort)MethodImplAttributes.Native) == 0 && + (impl_attributes & (ushort)MethodImplAttributes.Unmanaged) == 0 && + (impl_attributes & (ushort)MethodImplAttributes.Runtime) == 0; + } + } + + public MethodBody Body { + get { + var local = this.body; + if (local != null) + return local; + + if (!HasBody) + return null; + + if (HasImage && rva != 0) + return Module.Read (ref body, this, (method, reader) => reader.ReadMethodBody (method)); + + Interlocked.CompareExchange (ref body, new MethodBody (this), null); + + return body; + } + set { + var module = this.Module; + if (module == null) { + body = value; + return; + } + + // we reset Body to null in ILSpy to save memory; so we need that operation to be thread-safe + lock (module.SyncRoot) { + body = value; + if (value == null) + this.debug_info = null; + } + } + } + + public MethodDebugInformation DebugInformation { + get { + Mixin.Read (Body); + + if (debug_info == null) { + Interlocked.CompareExchange (ref debug_info, new MethodDebugInformation (this), null); + } + + return debug_info; + } + set { + debug_info = value; + } + } + + public bool HasPInvokeInfo { + get { + if (pinvoke != null) + return true; + + return IsPInvokeImpl; + } + } + + public PInvokeInfo PInvokeInfo { + get { + if (pinvoke != null) + return pinvoke; + + if (HasImage && IsPInvokeImpl) + return Module.Read (ref pinvoke, this, (method, reader) => reader.ReadPInvokeInfo (method)); + + return null; + } + set { + IsPInvokeImpl = true; + pinvoke = value; + } + } + + public bool HasOverrides { + get { + if (overrides != null) + return overrides.Count > 0; + + return HasImage && Module.Read (this, (method, reader) => reader.HasOverrides (method)); + } + } + + public Collection Overrides { + get { + if (overrides != null) + return overrides; + + if (HasImage) + return Module.Read (ref overrides, this, (method, reader) => reader.ReadOverrides (method)); + + Interlocked.CompareExchange (ref overrides, new Collection (), null); + + return overrides; + } + } + + public override bool HasGenericParameters { + get { + if (generic_parameters != null) + return generic_parameters.Count > 0; + + return this.GetHasGenericParameters (Module); + } + } + + public override Collection GenericParameters { + get { return generic_parameters ?? (this.GetGenericParameters (ref generic_parameters, Module)); } + } + + public bool HasCustomDebugInformations { + get { + Mixin.Read (Body); + + return !custom_infos.IsNullOrEmpty (); + } + } + + public Collection CustomDebugInformations { + get { + Mixin.Read (Body); + + if (custom_infos == null) + Interlocked.CompareExchange (ref custom_infos, new Collection (), null); + + return custom_infos; + } + } + + #region MethodAttributes + + public bool IsCompilerControlled { + get { return attributes.GetMaskedAttributes ((ushort)MethodAttributes.MemberAccessMask, (ushort)MethodAttributes.CompilerControlled); } + set { attributes = attributes.SetMaskedAttributes ((ushort)MethodAttributes.MemberAccessMask, (ushort)MethodAttributes.CompilerControlled, value); } + } + + public bool IsPrivate { + get { return attributes.GetMaskedAttributes ((ushort)MethodAttributes.MemberAccessMask, (ushort)MethodAttributes.Private); } + set { attributes = attributes.SetMaskedAttributes ((ushort)MethodAttributes.MemberAccessMask, (ushort)MethodAttributes.Private, value); } + } + + public bool IsFamilyAndAssembly { + get { return attributes.GetMaskedAttributes ((ushort)MethodAttributes.MemberAccessMask, (ushort)MethodAttributes.FamANDAssem); } + set { attributes = attributes.SetMaskedAttributes ((ushort)MethodAttributes.MemberAccessMask, (ushort)MethodAttributes.FamANDAssem, value); } + } + + public bool IsAssembly { + get { return attributes.GetMaskedAttributes ((ushort)MethodAttributes.MemberAccessMask, (ushort)MethodAttributes.Assembly); } + set { attributes = attributes.SetMaskedAttributes ((ushort)MethodAttributes.MemberAccessMask, (ushort)MethodAttributes.Assembly, value); } + } + + public bool IsFamily { + get { return attributes.GetMaskedAttributes ((ushort)MethodAttributes.MemberAccessMask, (ushort)MethodAttributes.Family); } + set { attributes = attributes.SetMaskedAttributes ((ushort)MethodAttributes.MemberAccessMask, (ushort)MethodAttributes.Family, value); } + } + + public bool IsFamilyOrAssembly { + get { return attributes.GetMaskedAttributes ((ushort)MethodAttributes.MemberAccessMask, (ushort)MethodAttributes.FamORAssem); } + set { attributes = attributes.SetMaskedAttributes ((ushort)MethodAttributes.MemberAccessMask, (ushort)MethodAttributes.FamORAssem, value); } + } + + public bool IsPublic { + get { return attributes.GetMaskedAttributes ((ushort)MethodAttributes.MemberAccessMask, (ushort)MethodAttributes.Public); } + set { attributes = attributes.SetMaskedAttributes ((ushort)MethodAttributes.MemberAccessMask, (ushort)MethodAttributes.Public, value); } + } + + public bool IsStatic { + get { return attributes.GetAttributes ((ushort)MethodAttributes.Static); } + set { attributes = attributes.SetAttributes ((ushort)MethodAttributes.Static, value); } + } + + public bool IsFinal { + get { return attributes.GetAttributes ((ushort)MethodAttributes.Final); } + set { attributes = attributes.SetAttributes ((ushort)MethodAttributes.Final, value); } + } + + public bool IsVirtual { + get { return attributes.GetAttributes ((ushort)MethodAttributes.Virtual); } + set { attributes = attributes.SetAttributes ((ushort)MethodAttributes.Virtual, value); } + } + + public bool IsHideBySig { + get { return attributes.GetAttributes ((ushort)MethodAttributes.HideBySig); } + set { attributes = attributes.SetAttributes ((ushort)MethodAttributes.HideBySig, value); } + } + + public bool IsReuseSlot { + get { return attributes.GetMaskedAttributes ((ushort)MethodAttributes.VtableLayoutMask, (ushort)MethodAttributes.ReuseSlot); } + set { attributes = attributes.SetMaskedAttributes ((ushort)MethodAttributes.VtableLayoutMask, (ushort)MethodAttributes.ReuseSlot, value); } + } + + public bool IsNewSlot { + get { return attributes.GetMaskedAttributes ((ushort)MethodAttributes.VtableLayoutMask, (ushort)MethodAttributes.NewSlot); } + set { attributes = attributes.SetMaskedAttributes ((ushort)MethodAttributes.VtableLayoutMask, (ushort)MethodAttributes.NewSlot, value); } + } + + public bool IsCheckAccessOnOverride { + get { return attributes.GetAttributes ((ushort)MethodAttributes.CheckAccessOnOverride); } + set { attributes = attributes.SetAttributes ((ushort)MethodAttributes.CheckAccessOnOverride, value); } + } + + public bool IsAbstract { + get { return attributes.GetAttributes ((ushort)MethodAttributes.Abstract); } + set { attributes = attributes.SetAttributes ((ushort)MethodAttributes.Abstract, value); } + } + + public bool IsSpecialName { + get { return attributes.GetAttributes ((ushort)MethodAttributes.SpecialName); } + set { attributes = attributes.SetAttributes ((ushort)MethodAttributes.SpecialName, value); } + } + + public bool IsPInvokeImpl { + get { return attributes.GetAttributes ((ushort)MethodAttributes.PInvokeImpl); } + set { attributes = attributes.SetAttributes ((ushort)MethodAttributes.PInvokeImpl, value); } + } + + public bool IsUnmanagedExport { + get { return attributes.GetAttributes ((ushort)MethodAttributes.UnmanagedExport); } + set { attributes = attributes.SetAttributes ((ushort)MethodAttributes.UnmanagedExport, value); } + } + + public bool IsRuntimeSpecialName { + get { return attributes.GetAttributes ((ushort)MethodAttributes.RTSpecialName); } + set { attributes = attributes.SetAttributes ((ushort)MethodAttributes.RTSpecialName, value); } + } + + public bool HasSecurity { + get { return attributes.GetAttributes ((ushort)MethodAttributes.HasSecurity); } + set { attributes = attributes.SetAttributes ((ushort)MethodAttributes.HasSecurity, value); } + } + + #endregion + + #region MethodImplAttributes + + public bool IsIL { + get { return impl_attributes.GetMaskedAttributes ((ushort)MethodImplAttributes.CodeTypeMask, (ushort)MethodImplAttributes.IL); } + set { impl_attributes = impl_attributes.SetMaskedAttributes ((ushort)MethodImplAttributes.CodeTypeMask, (ushort)MethodImplAttributes.IL, value); } + } + + public bool IsNative { + get { return impl_attributes.GetMaskedAttributes ((ushort)MethodImplAttributes.CodeTypeMask, (ushort)MethodImplAttributes.Native); } + set { impl_attributes = impl_attributes.SetMaskedAttributes ((ushort)MethodImplAttributes.CodeTypeMask, (ushort)MethodImplAttributes.Native, value); } + } + + public bool IsRuntime { + get { return impl_attributes.GetMaskedAttributes ((ushort)MethodImplAttributes.CodeTypeMask, (ushort)MethodImplAttributes.Runtime); } + set { impl_attributes = impl_attributes.SetMaskedAttributes ((ushort)MethodImplAttributes.CodeTypeMask, (ushort)MethodImplAttributes.Runtime, value); } + } + + public bool IsUnmanaged { + get { return impl_attributes.GetMaskedAttributes ((ushort)MethodImplAttributes.ManagedMask, (ushort)MethodImplAttributes.Unmanaged); } + set { impl_attributes = impl_attributes.SetMaskedAttributes ((ushort)MethodImplAttributes.ManagedMask, (ushort)MethodImplAttributes.Unmanaged, value); } + } + + public bool IsManaged { + get { return impl_attributes.GetMaskedAttributes ((ushort)MethodImplAttributes.ManagedMask, (ushort)MethodImplAttributes.Managed); } + set { impl_attributes = impl_attributes.SetMaskedAttributes ((ushort)MethodImplAttributes.ManagedMask, (ushort)MethodImplAttributes.Managed, value); } + } + + public bool IsForwardRef { + get { return impl_attributes.GetAttributes ((ushort)MethodImplAttributes.ForwardRef); } + set { impl_attributes = impl_attributes.SetAttributes ((ushort)MethodImplAttributes.ForwardRef, value); } + } + + public bool IsPreserveSig { + get { return impl_attributes.GetAttributes ((ushort)MethodImplAttributes.PreserveSig); } + set { impl_attributes = impl_attributes.SetAttributes ((ushort)MethodImplAttributes.PreserveSig, value); } + } + + public bool IsInternalCall { + get { return impl_attributes.GetAttributes ((ushort)MethodImplAttributes.InternalCall); } + set { impl_attributes = impl_attributes.SetAttributes ((ushort)MethodImplAttributes.InternalCall, value); } + } + + public bool IsSynchronized { + get { return impl_attributes.GetAttributes ((ushort)MethodImplAttributes.Synchronized); } + set { impl_attributes = impl_attributes.SetAttributes ((ushort)MethodImplAttributes.Synchronized, value); } + } + + public bool NoInlining { + get { return impl_attributes.GetAttributes ((ushort)MethodImplAttributes.NoInlining); } + set { impl_attributes = impl_attributes.SetAttributes ((ushort)MethodImplAttributes.NoInlining, value); } + } + + public bool NoOptimization { + get { return impl_attributes.GetAttributes ((ushort)MethodImplAttributes.NoOptimization); } + set { impl_attributes = impl_attributes.SetAttributes ((ushort)MethodImplAttributes.NoOptimization, value); } + } + + public bool AggressiveInlining { + get { return impl_attributes.GetAttributes ((ushort)MethodImplAttributes.AggressiveInlining); } + set { impl_attributes = impl_attributes.SetAttributes ((ushort)MethodImplAttributes.AggressiveInlining, value); } + } + + #endregion + + #region MethodSemanticsAttributes + + public bool IsSetter { + get { return this.GetSemantics (MethodSemanticsAttributes.Setter); } + set { this.SetSemantics (MethodSemanticsAttributes.Setter, value); } + } + + public bool IsGetter { + get { return this.GetSemantics (MethodSemanticsAttributes.Getter); } + set { this.SetSemantics (MethodSemanticsAttributes.Getter, value); } + } + + public bool IsOther { + get { return this.GetSemantics (MethodSemanticsAttributes.Other); } + set { this.SetSemantics (MethodSemanticsAttributes.Other, value); } + } + + public bool IsAddOn { + get { return this.GetSemantics (MethodSemanticsAttributes.AddOn); } + set { this.SetSemantics (MethodSemanticsAttributes.AddOn, value); } + } + + public bool IsRemoveOn { + get { return this.GetSemantics (MethodSemanticsAttributes.RemoveOn); } + set { this.SetSemantics (MethodSemanticsAttributes.RemoveOn, value); } + } + + public bool IsFire { + get { return this.GetSemantics (MethodSemanticsAttributes.Fire); } + set { this.SetSemantics (MethodSemanticsAttributes.Fire, value); } + } + + #endregion + + public new TypeDefinition DeclaringType { + get { return (TypeDefinition)base.DeclaringType; } + set { base.DeclaringType = value; } + } + + public bool IsConstructor { + get { + return this.IsRuntimeSpecialName + && this.IsSpecialName + && (this.Name == ".cctor" || this.Name == ".ctor"); + } + } + + public override bool IsDefinition { + get { return true; } + } + + internal MethodDefinition () + { + this.token = new MetadataToken (TokenType.Method); + } + + public MethodDefinition (string name, MethodAttributes attributes, TypeReference returnType) + : base (name, returnType) + { + this.attributes = (ushort)attributes; + this.HasThis = !this.IsStatic; + this.token = new MetadataToken (TokenType.Method); + } + + public override MethodDefinition Resolve () + { + return this; + } + } + + static partial class Mixin { + + public static ParameterDefinition GetParameter (this MethodBody self, int index) + { + var method = self.method; + + if (method.HasThis) { + if (index == 0) + return self.ThisParameter; + + index--; + } + + var parameters = method.Parameters; + + if (index < 0 || index >= parameters.size) + return null; + + return parameters [index]; + } + + public static VariableDefinition GetVariable (this MethodBody self, int index) + { + var variables = self.Variables; + + if (index < 0 || index >= variables.size) + return null; + + return variables [index]; + } + + public static bool GetSemantics (this MethodDefinition self, MethodSemanticsAttributes semantics) + { + return (self.SemanticsAttributes & semantics) != 0; + } + + public static void SetSemantics (this MethodDefinition self, MethodSemanticsAttributes semantics, bool value) + { + if (value) + self.SemanticsAttributes |= semantics; + else + self.SemanticsAttributes &= ~semantics; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodDefinition.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodDefinition.cs.meta new file mode 100644 index 0000000..9fbc7fd --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodDefinition.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 51ade7e624f84ae4abb8dc28a51b3082 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodImplAttributes.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodImplAttributes.cs new file mode 100644 index 0000000..722d5c5 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodImplAttributes.cs @@ -0,0 +1,36 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil { + + [Flags] + public enum MethodImplAttributes : ushort { + CodeTypeMask = 0x0003, + IL = 0x0000, // Method impl is CIL + Native = 0x0001, // Method impl is native + OPTIL = 0x0002, // Reserved: shall be zero in conforming implementations + Runtime = 0x0003, // Method impl is provided by the runtime + + ManagedMask = 0x0004, // Flags specifying whether the code is managed or unmanaged + Unmanaged = 0x0004, // Method impl is unmanaged, otherwise managed + Managed = 0x0000, // Method impl is managed + + // Implementation info and interop + ForwardRef = 0x0010, // Indicates method is defined; used primarily in merge scenarios + PreserveSig = 0x0080, // Reserved: conforming implementations may ignore + InternalCall = 0x1000, // Reserved: shall be zero in conforming implementations + Synchronized = 0x0020, // Method is single threaded through the body + NoOptimization = 0x0040, // Method is not optimized by the JIT. + NoInlining = 0x0008, // Method may not be inlined + AggressiveInlining = 0x0100, // Method should be inlined, if possible. + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodImplAttributes.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodImplAttributes.cs.meta new file mode 100644 index 0000000..bc1d0fc --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodImplAttributes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bbe7f8d150d8c6b48abd24ac7718c9f4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReference.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReference.cs new file mode 100644 index 0000000..6280983 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReference.cs @@ -0,0 +1,202 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System; +using System.Text; +using System.Threading; + +namespace MonoFN.Cecil { + + public class MethodReference : MemberReference, IMethodSignature, IGenericParameterProvider, IGenericContext { + + internal ParameterDefinitionCollection parameters; + MethodReturnType return_type; + + bool has_this; + bool explicit_this; + MethodCallingConvention calling_convention; + internal Collection generic_parameters; + + public virtual bool HasThis { + get { return has_this; } + set { has_this = value; } + } + + public virtual bool ExplicitThis { + get { return explicit_this; } + set { explicit_this = value; } + } + + public virtual MethodCallingConvention CallingConvention { + get { return calling_convention; } + set { calling_convention = value; } + } + + public virtual bool HasParameters { + get { return !parameters.IsNullOrEmpty (); } + } + + public virtual Collection Parameters { + get { + if (parameters == null) + Interlocked.CompareExchange (ref parameters, new ParameterDefinitionCollection (this), null); + + return parameters; + } + } + + IGenericParameterProvider IGenericContext.Type { + get { + var declaring_type = this.DeclaringType; + var instance = declaring_type as GenericInstanceType; + if (instance != null) + return instance.ElementType; + + return declaring_type; + } + } + + IGenericParameterProvider IGenericContext.Method { + get { return this; } + } + + GenericParameterType IGenericParameterProvider.GenericParameterType { + get { return GenericParameterType.Method; } + } + + public virtual bool HasGenericParameters { + get { return !generic_parameters.IsNullOrEmpty (); } + } + + public virtual Collection GenericParameters { + get { + if (generic_parameters == null) + Interlocked.CompareExchange (ref generic_parameters, new GenericParameterCollection (this), null); + + return generic_parameters; + } + } + + public TypeReference ReturnType { + get { + var return_type = MethodReturnType; + return return_type != null ? return_type.ReturnType : null; + } + set { + var return_type = MethodReturnType; + if (return_type != null) + return_type.ReturnType = value; + } + } + + public virtual MethodReturnType MethodReturnType { + get { return return_type; } + set { return_type = value; } + } + + public override string FullName { + get { + var builder = new StringBuilder (); + builder.Append (ReturnType.FullName) + .Append (" ") + .Append (MemberFullName ()); + this.MethodSignatureFullName (builder); + return builder.ToString (); + } + } + + public virtual bool IsGenericInstance { + get { return false; } + } + + public override bool ContainsGenericParameter { + get { + if (this.ReturnType.ContainsGenericParameter || base.ContainsGenericParameter) + return true; + + if (!HasParameters) + return false; + + var parameters = this.Parameters; + + for (int i = 0; i < parameters.Count; i++) + if (parameters [i].ParameterType.ContainsGenericParameter) + return true; + + return false; + } + } + + internal MethodReference () + { + this.return_type = new MethodReturnType (this); + this.token = new MetadataToken (TokenType.MemberRef); + } + + public MethodReference (string name, TypeReference returnType) + : base (name) + { + Mixin.CheckType (returnType, Mixin.Argument.returnType); + + this.return_type = new MethodReturnType (this); + this.return_type.ReturnType = returnType; + this.token = new MetadataToken (TokenType.MemberRef); + } + + public MethodReference (string name, TypeReference returnType, TypeReference declaringType) + : this (name, returnType) + { + Mixin.CheckType (declaringType, Mixin.Argument.declaringType); + + this.DeclaringType = declaringType; + } + + public virtual MethodReference GetElementMethod () + { + return this; + } + + protected override IMemberDefinition ResolveDefinition () + { + return this.Resolve (); + } + + public new virtual MethodDefinition Resolve () + { + var module = this.Module; + if (module == null) + throw new NotSupportedException (); + + return module.Resolve (this); + } + } + + static partial class Mixin { + + public static bool IsVarArg (this IMethodSignature self) + { + return self.CallingConvention == MethodCallingConvention.VarArg; + } + + public static int GetSentinelPosition (this IMethodSignature self) + { + if (!self.HasParameters) + return -1; + + var parameters = self.Parameters; + for (int i = 0; i < parameters.Count; i++) + if (parameters [i].ParameterType.IsSentinel) + return i; + + return -1; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReference.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReference.cs.meta new file mode 100644 index 0000000..2f0eb25 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReference.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e894eb84c10b07f42bf952d556a35c1c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReferenceComparer.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReferenceComparer.cs new file mode 100644 index 0000000..e8df3fd --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReferenceComparer.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; + +namespace MonoFN.Cecil { + internal sealed class MethodReferenceComparer : EqualityComparer { + // Initialized lazily for each thread + [ThreadStatic] + static List xComparisonStack = null; + + [ThreadStatic] + static List yComparisonStack = null; + + public override bool Equals (MethodReference x, MethodReference y) + { + return AreEqual (x, y); + } + + public override int GetHashCode (MethodReference obj) + { + return GetHashCodeFor (obj); + } + + public static bool AreEqual (MethodReference x, MethodReference y) + { + if (ReferenceEquals (x, y)) + return true; + + if (x.HasThis != y.HasThis) + return false; + + if (x.HasParameters != y.HasParameters) + return false; + + if (x.HasGenericParameters != y.HasGenericParameters) + return false; + + if (x.Parameters.Count != y.Parameters.Count) + return false; + + if (x.Name != y.Name) + return false; + + if (!TypeReferenceEqualityComparer.AreEqual (x.DeclaringType, y.DeclaringType)) + return false; + + var xGeneric = x as GenericInstanceMethod; + var yGeneric = y as GenericInstanceMethod; + if (xGeneric != null || yGeneric != null) { + if (xGeneric == null || yGeneric == null) + return false; + + if (xGeneric.GenericArguments.Count != yGeneric.GenericArguments.Count) + return false; + + for (int i = 0; i < xGeneric.GenericArguments.Count; i++) + if (!TypeReferenceEqualityComparer.AreEqual (xGeneric.GenericArguments [i], yGeneric.GenericArguments [i])) + return false; + } + + var xResolved = x.Resolve (); + var yResolved = y.Resolve (); + + if (xResolved != yResolved) + return false; + + if (xResolved == null) { + // We couldn't resolve either method. In order for them to be equal, their parameter types _must_ match. But wait, there's a twist! + // There exists a situation where we might get into a recursive state: parameter type comparison might lead to comparing the same + // methods again if the parameter types are generic parameters whose owners are these methods. We guard against these by using a + // thread static list of all our comparisons carried out in the stack so far, and if we're in progress of comparing them already, + // we'll just say that they match. + + if (xComparisonStack == null) + xComparisonStack = new List (); + + if (yComparisonStack == null) + yComparisonStack = new List (); + + for (int i = 0; i < xComparisonStack.Count; i++) { + if (xComparisonStack [i] == x && yComparisonStack [i] == y) + return true; + } + + xComparisonStack.Add (x); + + try { + yComparisonStack.Add (y); + + try { + for (int i = 0; i < x.Parameters.Count; i++) { + if (!TypeReferenceEqualityComparer.AreEqual (x.Parameters [i].ParameterType, y.Parameters [i].ParameterType)) + return false; + } + } + finally { + yComparisonStack.RemoveAt (yComparisonStack.Count - 1); + } + } + finally { + xComparisonStack.RemoveAt (xComparisonStack.Count - 1); + } + } + + return true; + } + + public static bool AreSignaturesEqual (MethodReference x, MethodReference y, TypeComparisonMode comparisonMode = TypeComparisonMode.Exact) + { + if (x.HasThis != y.HasThis) + return false; + + if (x.Parameters.Count != y.Parameters.Count) + return false; + + if (x.GenericParameters.Count != y.GenericParameters.Count) + return false; + + for (var i = 0; i < x.Parameters.Count; i++) + if (!TypeReferenceEqualityComparer.AreEqual (x.Parameters [i].ParameterType, y.Parameters [i].ParameterType, comparisonMode)) + return false; + + if (!TypeReferenceEqualityComparer.AreEqual (x.ReturnType, y.ReturnType, comparisonMode)) + return false; + + return true; + } + + public static int GetHashCodeFor (MethodReference obj) + { + // a very good prime number + const int hashCodeMultiplier = 486187739; + + var genericInstanceMethod = obj as GenericInstanceMethod; + if (genericInstanceMethod != null) { + var hashCode = GetHashCodeFor (genericInstanceMethod.ElementMethod); + for (var i = 0; i < genericInstanceMethod.GenericArguments.Count; i++) + hashCode = hashCode * hashCodeMultiplier + TypeReferenceEqualityComparer.GetHashCodeFor (genericInstanceMethod.GenericArguments [i]); + return hashCode; + } + + return TypeReferenceEqualityComparer.GetHashCodeFor (obj.DeclaringType) * hashCodeMultiplier + obj.Name.GetHashCode (); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReferenceComparer.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReferenceComparer.cs.meta new file mode 100644 index 0000000..901496a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReferenceComparer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e61a3569af766524884d80a918529ab8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReturnType.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReturnType.cs new file mode 100644 index 0000000..c833565 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReturnType.cs @@ -0,0 +1,97 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System.Threading; + +namespace MonoFN.Cecil { + + public sealed class MethodReturnType : IConstantProvider, ICustomAttributeProvider, IMarshalInfoProvider { + + internal IMethodSignature method; + internal ParameterDefinition parameter; + TypeReference return_type; + + public IMethodSignature Method { + get { return method; } + } + + public TypeReference ReturnType { + get { return return_type; } + set { return_type = value; } + } + + internal ParameterDefinition Parameter { + get { + if (parameter == null) + Interlocked.CompareExchange (ref parameter, new ParameterDefinition (return_type, method), null); + + return parameter; + } + } + + public MetadataToken MetadataToken { + get { return Parameter.MetadataToken; } + set { Parameter.MetadataToken = value; } + } + + public ParameterAttributes Attributes { + get { return Parameter.Attributes; } + set { Parameter.Attributes = value; } + } + + public string Name { + get { return Parameter.Name; } + set { Parameter.Name = value; } + } + + public bool HasCustomAttributes { + get { return parameter != null && parameter.HasCustomAttributes; } + } + + public Collection CustomAttributes { + get { return Parameter.CustomAttributes; } + } + + public bool HasDefault { + get { return parameter != null && parameter.HasDefault; } + set { Parameter.HasDefault = value; } + } + + public bool HasConstant { + get { return parameter != null && parameter.HasConstant; } + set { Parameter.HasConstant = value; } + } + + public object Constant { + get { return Parameter.Constant; } + set { Parameter.Constant = value; } + } + + public bool HasFieldMarshal { + get { return parameter != null && parameter.HasFieldMarshal; } + set { Parameter.HasFieldMarshal = value; } + } + + public bool HasMarshalInfo { + get { return parameter != null && parameter.HasMarshalInfo; } + } + + public MarshalInfo MarshalInfo { + get { return Parameter.MarshalInfo; } + set { Parameter.MarshalInfo = value; } + } + + public MethodReturnType (IMethodSignature method) + { + this.method = method; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReturnType.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReturnType.cs.meta new file mode 100644 index 0000000..1cab923 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReturnType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 51d33467d0e0c6f46a788d5da23d7b23 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodSemanticsAttributes.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodSemanticsAttributes.cs new file mode 100644 index 0000000..10b9ef9 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodSemanticsAttributes.cs @@ -0,0 +1,25 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil { + + [Flags] + public enum MethodSemanticsAttributes : ushort { + None = 0x0000, + Setter = 0x0001, // Setter for property + Getter = 0x0002, // Getter for property + Other = 0x0004, // Other method for property or event + AddOn = 0x0008, // AddOn method for event + RemoveOn = 0x0010, // RemoveOn method for event + Fire = 0x0020 // Fire method for event + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodSemanticsAttributes.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodSemanticsAttributes.cs.meta new file mode 100644 index 0000000..7e68665 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodSemanticsAttributes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d6ed7cb61b1647749854923d7f4f2c4e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodSpecification.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodSpecification.cs new file mode 100644 index 0000000..b4bc4ed --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodSpecification.cs @@ -0,0 +1,83 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System; + +namespace MonoFN.Cecil { + + public abstract class MethodSpecification : MethodReference { + + readonly MethodReference method; + + public MethodReference ElementMethod { + get { return method; } + } + + public override string Name { + get { return method.Name; } + set { throw new InvalidOperationException (); } + } + + public override MethodCallingConvention CallingConvention { + get { return method.CallingConvention; } + set { throw new InvalidOperationException (); } + } + + public override bool HasThis { + get { return method.HasThis; } + set { throw new InvalidOperationException (); } + } + + public override bool ExplicitThis { + get { return method.ExplicitThis; } + set { throw new InvalidOperationException (); } + } + + public override MethodReturnType MethodReturnType { + get { return method.MethodReturnType; } + set { throw new InvalidOperationException (); } + } + + public override TypeReference DeclaringType { + get { return method.DeclaringType; } + set { throw new InvalidOperationException (); } + } + + public override ModuleDefinition Module { + get { return method.Module; } + } + + public override bool HasParameters { + get { return method.HasParameters; } + } + + public override Collection Parameters { + get { return method.Parameters; } + } + + public override bool ContainsGenericParameter { + get { return method.ContainsGenericParameter; } + } + + internal MethodSpecification (MethodReference method) + { + Mixin.CheckMethod (method); + + this.method = method; + this.token = new MetadataToken (TokenType.MethodSpec); + } + + public sealed override MethodReference GetElementMethod () + { + return method.GetElementMethod (); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodSpecification.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodSpecification.cs.meta new file mode 100644 index 0000000..0ab0565 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodSpecification.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aab27df5116b6ef46ac37993c843e4f0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Modifiers.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Modifiers.cs new file mode 100644 index 0000000..13367cd --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Modifiers.cs @@ -0,0 +1,112 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +using MD = MonoFN.Cecil.Metadata; + +namespace MonoFN.Cecil { + + public interface IModifierType { + TypeReference ModifierType { get; } + TypeReference ElementType { get; } + } + + public sealed class OptionalModifierType : TypeSpecification, IModifierType { + + TypeReference modifier_type; + + public TypeReference ModifierType { + get { return modifier_type; } + set { modifier_type = value; } + } + + public override string Name { + get { return base.Name + Suffix; } + } + + public override string FullName { + get { return base.FullName + Suffix; } + } + + string Suffix { + get { return " modopt(" + modifier_type + ")"; } + } + + public override bool IsValueType { + get { return false; } + set { throw new InvalidOperationException (); } + } + + public override bool IsOptionalModifier { + get { return true; } + } + + public override bool ContainsGenericParameter { + get { return modifier_type.ContainsGenericParameter || base.ContainsGenericParameter; } + } + + public OptionalModifierType (TypeReference modifierType, TypeReference type) + : base (type) + { + if (modifierType == null) + throw new ArgumentNullException (Mixin.Argument.modifierType.ToString ()); + Mixin.CheckType (type); + this.modifier_type = modifierType; + this.etype = MD.ElementType.CModOpt; + } + } + + public sealed class RequiredModifierType : TypeSpecification, IModifierType { + + TypeReference modifier_type; + + public TypeReference ModifierType { + get { return modifier_type; } + set { modifier_type = value; } + } + + public override string Name { + get { return base.Name + Suffix; } + } + + public override string FullName { + get { return base.FullName + Suffix; } + } + + string Suffix { + get { return " modreq(" + modifier_type + ")"; } + } + + public override bool IsValueType { + get { return false; } + set { throw new InvalidOperationException (); } + } + + public override bool IsRequiredModifier { + get { return true; } + } + + public override bool ContainsGenericParameter { + get { return modifier_type.ContainsGenericParameter || base.ContainsGenericParameter; } + } + + public RequiredModifierType (TypeReference modifierType, TypeReference type) + : base (type) + { + if (modifierType == null) + throw new ArgumentNullException (Mixin.Argument.modifierType.ToString ()); + Mixin.CheckType (type); + this.modifier_type = modifierType; + this.etype = MD.ElementType.CModReqD; + } + + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Modifiers.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Modifiers.cs.meta new file mode 100644 index 0000000..7678d47 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Modifiers.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1fc5ce450d74933428a568f51cb49c16 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleDefinition.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleDefinition.cs new file mode 100644 index 0000000..9af60a5 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleDefinition.cs @@ -0,0 +1,1353 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.Cil; +using MonoFN.Cecil.Metadata; +using MonoFN.Cecil.PE; +using MonoFN.Collections.Generic; +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using SR = System.Reflection; + +namespace MonoFN.Cecil { + + public enum ReadingMode { + Immediate = 1, + Deferred = 2, + } + + public sealed class ReaderParameters { + + ReadingMode reading_mode; + internal IAssemblyResolver assembly_resolver; + internal IMetadataResolver metadata_resolver; + internal IMetadataImporterProvider metadata_importer_provider; + internal IReflectionImporterProvider reflection_importer_provider; + Stream symbol_stream; + ISymbolReaderProvider symbol_reader_provider; + bool read_symbols; + bool throw_symbols_mismatch; + bool projections; + bool in_memory; + bool read_write; + + public ReadingMode ReadingMode { + get { return reading_mode; } + set { reading_mode = value; } + } + + public bool InMemory { + get { return in_memory; } + set { in_memory = value; } + } + + public IAssemblyResolver AssemblyResolver { + get { return assembly_resolver; } + set { assembly_resolver = value; } + } + + public IMetadataResolver MetadataResolver { + get { return metadata_resolver; } + set { metadata_resolver = value; } + } + + public IMetadataImporterProvider MetadataImporterProvider { + get { return metadata_importer_provider; } + set { metadata_importer_provider = value; } + } + + public IReflectionImporterProvider ReflectionImporterProvider { + get { return reflection_importer_provider; } + set { reflection_importer_provider = value; } + } + + public Stream SymbolStream { + get { return symbol_stream; } + set { symbol_stream = value; } + } + + public ISymbolReaderProvider SymbolReaderProvider { + get { return symbol_reader_provider; } + set { symbol_reader_provider = value; } + } + + public bool ReadSymbols { + get { return read_symbols; } + set { read_symbols = value; } + } + + public bool ThrowIfSymbolsAreNotMatching { + get { return throw_symbols_mismatch; } + set { throw_symbols_mismatch = value; } + } + + public bool ReadWrite { + get { return read_write; } + set { read_write = value; } + } + + public bool ApplyWindowsRuntimeProjections { + get { return projections; } + set { projections = value; } + } + + public ReaderParameters () + : this (ReadingMode.Deferred) + { + } + + public ReaderParameters (ReadingMode readingMode) + { + this.reading_mode = readingMode; + this.throw_symbols_mismatch = true; + } + } + + public sealed class ModuleParameters { + + ModuleKind kind; + TargetRuntime runtime; + uint? timestamp; + TargetArchitecture architecture; + IAssemblyResolver assembly_resolver; + IMetadataResolver metadata_resolver; + IMetadataImporterProvider metadata_importer_provider; + IReflectionImporterProvider reflection_importer_provider; + + public ModuleKind Kind { + get { return kind; } + set { kind = value; } + } + + public TargetRuntime Runtime { + get { return runtime; } + set { runtime = value; } + } + + public uint? Timestamp { + get { return timestamp; } + set { timestamp = value; } + } + + public TargetArchitecture Architecture { + get { return architecture; } + set { architecture = value; } + } + + public IAssemblyResolver AssemblyResolver { + get { return assembly_resolver; } + set { assembly_resolver = value; } + } + + public IMetadataResolver MetadataResolver { + get { return metadata_resolver; } + set { metadata_resolver = value; } + } + + public IMetadataImporterProvider MetadataImporterProvider { + get { return metadata_importer_provider; } + set { metadata_importer_provider = value; } + } + + public IReflectionImporterProvider ReflectionImporterProvider { + get { return reflection_importer_provider; } + set { reflection_importer_provider = value; } + } + + public ModuleParameters () + { + this.kind = ModuleKind.Dll; + this.Runtime = GetCurrentRuntime (); + this.architecture = TargetArchitecture.I386; + } + + static TargetRuntime GetCurrentRuntime () + { + return typeof (object).Assembly.ImageRuntimeVersion.ParseRuntime (); + } + } + + public sealed class WriterParameters { + + uint? timestamp; + Stream symbol_stream; + ISymbolWriterProvider symbol_writer_provider; + bool write_symbols; + byte [] key_blob; + string key_container; + SR.StrongNameKeyPair key_pair; + + public uint? Timestamp { + get { return timestamp; } + set { timestamp = value; } + } + + public Stream SymbolStream { + get { return symbol_stream; } + set { symbol_stream = value; } + } + + public ISymbolWriterProvider SymbolWriterProvider { + get { return symbol_writer_provider; } + set { symbol_writer_provider = value; } + } + + public bool WriteSymbols { + get { return write_symbols; } + set { write_symbols = value; } + } + + public bool HasStrongNameKey { + get { return key_pair != null || key_blob != null || key_container != null; } + } + + public byte [] StrongNameKeyBlob { + get { return key_blob; } + set { key_blob = value; } + } + + public string StrongNameKeyContainer { + get { return key_container; } + set { key_container = value; } + } + + public SR.StrongNameKeyPair StrongNameKeyPair { + get { return key_pair; } + set { key_pair = value; } + } + + public bool DeterministicMvid { get; set; } + } + + public sealed class ModuleDefinition : ModuleReference, ICustomAttributeProvider, ICustomDebugInformationProvider, IDisposable { + + internal Image Image; + internal MetadataSystem MetadataSystem; + internal ReadingMode ReadingMode; + internal ISymbolReaderProvider SymbolReaderProvider; + + internal ISymbolReader symbol_reader; + internal Disposable assembly_resolver; + internal IMetadataResolver metadata_resolver; + internal TypeSystem type_system; + internal readonly MetadataReader reader; + readonly string file_name; + + internal string runtime_version; + internal ModuleKind kind; + WindowsRuntimeProjections projections; + MetadataKind metadata_kind; + TargetRuntime runtime; + TargetArchitecture architecture; + ModuleAttributes attributes; + ModuleCharacteristics characteristics; + Guid mvid; + + internal ushort linker_version = 8; + internal ushort subsystem_major = 4; + internal ushort subsystem_minor = 0; + internal uint timestamp; + + internal AssemblyDefinition assembly; + MethodDefinition entry_point; + bool entry_point_set; + + internal IReflectionImporter reflection_importer; + internal IMetadataImporter metadata_importer; + + Collection custom_attributes; + Collection references; + Collection modules; + Collection resources; + Collection exported_types; + TypeDefinitionCollection types; + + internal Collection custom_infos; + + internal MetadataBuilder metadata_builder; + + public bool IsMain { + get { return kind != ModuleKind.NetModule; } + } + + public ModuleKind Kind { + get { return kind; } + set { kind = value; } + } + + public MetadataKind MetadataKind { + get { return metadata_kind; } + set { metadata_kind = value; } + } + + internal WindowsRuntimeProjections Projections { + get { + if (projections == null) + Interlocked.CompareExchange (ref projections, new WindowsRuntimeProjections (this), null); + + return projections; + } + } + + public TargetRuntime Runtime { + get { return runtime; } + set { + runtime = value; + runtime_version = runtime.RuntimeVersionString (); + } + } + + public string RuntimeVersion { + get { return runtime_version; } + set { + runtime_version = value; + runtime = runtime_version.ParseRuntime (); + } + } + + public TargetArchitecture Architecture { + get { return architecture; } + set { architecture = value; } + } + + public ModuleAttributes Attributes { + get { return attributes; } + set { attributes = value; } + } + + public ModuleCharacteristics Characteristics { + get { return characteristics; } + set { characteristics = value; } + } + + [Obsolete ("Use FileName")] + public string FullyQualifiedName { + get { return file_name; } + } + + public string FileName { + get { return file_name; } + } + + public Guid Mvid { + get { return mvid; } + set { mvid = value; } + } + + internal bool HasImage { + get { return Image != null; } + } + + public bool HasSymbols { + get { return symbol_reader != null; } + } + + public ISymbolReader SymbolReader { + get { return symbol_reader; } + } + + public override MetadataScopeType MetadataScopeType { + get { return MetadataScopeType.ModuleDefinition; } + } + + public AssemblyDefinition Assembly { + get { return assembly; } + } + + internal IReflectionImporter ReflectionImporter { + get { + if (reflection_importer == null) + Interlocked.CompareExchange (ref reflection_importer, new DefaultReflectionImporter (this), null); + + return reflection_importer; + } + } + + internal IMetadataImporter MetadataImporter { + get { + if (metadata_importer == null) + Interlocked.CompareExchange (ref metadata_importer, new DefaultMetadataImporter (this), null); + + return metadata_importer; + } + } + + public IAssemblyResolver AssemblyResolver { + get { + if (assembly_resolver.value == null) { + lock (module_lock) { + assembly_resolver = Disposable.Owned (new DefaultAssemblyResolver () as IAssemblyResolver); + } + } + + return assembly_resolver.value; + } + } + + public IMetadataResolver MetadataResolver { + get { + if (metadata_resolver == null) + Interlocked.CompareExchange (ref metadata_resolver, new MetadataResolver (this.AssemblyResolver), null); + + return metadata_resolver; + } + } + + public TypeSystem TypeSystem { + get { + if (type_system == null) + Interlocked.CompareExchange (ref type_system, TypeSystem.CreateTypeSystem (this), null); + + return type_system; + } + } + + public bool HasAssemblyReferences { + get { + if (references != null) + return references.Count > 0; + + return HasImage && Image.HasTable (Table.AssemblyRef); + } + } + + public Collection AssemblyReferences { + get { + if (references != null) + return references; + + if (HasImage) + return Read (ref references, this, (_, reader) => reader.ReadAssemblyReferences ()); + + Interlocked.CompareExchange (ref references, new Collection (), null); + return references; + } + } + + public bool HasModuleReferences { + get { + if (modules != null) + return modules.Count > 0; + + return HasImage && Image.HasTable (Table.ModuleRef); + } + } + + public Collection ModuleReferences { + get { + if (modules != null) + return modules; + + if (HasImage) + return Read (ref modules, this, (_, reader) => reader.ReadModuleReferences ()); + + Interlocked.CompareExchange (ref modules, new Collection (), null); + return modules; + } + } + + public bool HasResources { + get { + if (resources != null) + return resources.Count > 0; + + if (HasImage) + return Image.HasTable (Table.ManifestResource) || Read (this, (_, reader) => reader.HasFileResource ()); + + return false; + } + } + + public Collection Resources { + get { + if (resources != null) + return resources; + + if (HasImage) + return Read (ref resources, this, (_, reader) => reader.ReadResources ()); + + Interlocked.CompareExchange (ref resources, new Collection (), null); + return resources; + } + } + + public bool HasCustomAttributes { + get { + if (custom_attributes != null) + return custom_attributes.Count > 0; + + return this.GetHasCustomAttributes (this); + } + } + + public Collection CustomAttributes { + get { return custom_attributes ?? (this.GetCustomAttributes (ref custom_attributes, this)); } + } + + public bool HasTypes { + get { + if (types != null) + return types.Count > 0; + + return HasImage && Image.HasTable (Table.TypeDef); + } + } + + public Collection Types { + get { + if (types != null) + return types; + + if (HasImage) + return Read (ref types, this, (_, reader) => reader.ReadTypes ()); + + Interlocked.CompareExchange (ref types, new TypeDefinitionCollection (this), null); + return types; + } + } + + public bool HasExportedTypes { + get { + if (exported_types != null) + return exported_types.Count > 0; + + return HasImage && Image.HasTable (Table.ExportedType); + } + } + + public Collection ExportedTypes { + get { + if (exported_types != null) + return exported_types; + + if (HasImage) + return Read (ref exported_types, this, (_, reader) => reader.ReadExportedTypes ()); + + Interlocked.CompareExchange (ref exported_types, new Collection (), null); + return exported_types; + } + } + + public MethodDefinition EntryPoint { + get { + if (entry_point_set) + return entry_point; + + if (HasImage) + Read (ref entry_point, this, (_, reader) => reader.ReadEntryPoint ()); + else + entry_point = null; + + entry_point_set = true; + return entry_point; + } + set { + entry_point = value; + entry_point_set = true; + } + } + + public bool HasCustomDebugInformations { + get { + return custom_infos != null && custom_infos.Count > 0; + } + } + + public Collection CustomDebugInformations { + get { + if (custom_infos == null) + Interlocked.CompareExchange (ref custom_infos, new Collection (), null); + + return custom_infos; + } + } + + internal ModuleDefinition () + { + this.MetadataSystem = new MetadataSystem (); + this.token = new MetadataToken (TokenType.Module, 1); + } + + internal ModuleDefinition (Image image) + : this () + { + this.Image = image; + this.kind = image.Kind; + this.RuntimeVersion = image.RuntimeVersion; + this.architecture = image.Architecture; + this.attributes = image.Attributes; + this.characteristics = image.DllCharacteristics; + this.linker_version = image.LinkerVersion; + this.subsystem_major = image.SubSystemMajor; + this.subsystem_minor = image.SubSystemMinor; + this.file_name = image.FileName; + this.timestamp = image.Timestamp; + + this.reader = new MetadataReader (this); + } + + public void Dispose () + { + if (Image != null) + Image.Dispose (); + + if (symbol_reader != null) + symbol_reader.Dispose (); + + if (assembly_resolver.value != null) + assembly_resolver.Dispose (); + } + + public bool HasTypeReference (string fullName) + { + return HasTypeReference (string.Empty, fullName); + } + + public bool HasTypeReference (string scope, string fullName) + { + Mixin.CheckFullName (fullName); + + if (!HasImage) + return false; + + return GetTypeReference (scope, fullName) != null; + } + + public bool TryGetTypeReference (string fullName, out TypeReference type) + { + return TryGetTypeReference (string.Empty, fullName, out type); + } + + public bool TryGetTypeReference (string scope, string fullName, out TypeReference type) + { + Mixin.CheckFullName (fullName); + + if (!HasImage) { + type = null; + return false; + } + + return (type = GetTypeReference (scope, fullName)) != null; + } + + TypeReference GetTypeReference (string scope, string fullname) + { + return Read (new Row (scope, fullname), (row, reader) => reader.GetTypeReference (row.Col1, row.Col2)); + } + + public IEnumerable GetTypeReferences () + { + if (!HasImage) + return Empty.Array; + + return Read (this, (_, reader) => reader.GetTypeReferences ()); + } + + public IEnumerable GetMemberReferences () + { + if (!HasImage) + return Empty.Array; + + return Read (this, (_, reader) => reader.GetMemberReferences ()); + } + + public IEnumerable GetCustomAttributes () + { + if (!HasImage) + return Empty.Array; + + return Read (this, (_, reader) => reader.GetCustomAttributes ()); + } + + public TypeReference GetType (string fullName, bool runtimeName) + { + return runtimeName + ? TypeParser.ParseType (this, fullName, typeDefinitionOnly: true) + : GetType (fullName); + } + + public TypeDefinition GetType (string fullName) + { + Mixin.CheckFullName (fullName); + + var position = fullName.IndexOf ('/'); + if (position > 0) + return GetNestedType (fullName); + + return ((TypeDefinitionCollection)this.Types).GetType (fullName); + } + + public TypeDefinition GetType (string @namespace, string name) + { + Mixin.CheckName (name); + + return ((TypeDefinitionCollection)this.Types).GetType (@namespace ?? string.Empty, name); + } + + public IEnumerable GetTypes () + { + return GetTypes (Types); + } + + static IEnumerable GetTypes (Collection types) + { + for (int i = 0; i < types.Count; i++) { + var type = types [i]; + + yield return type; + + if (!type.HasNestedTypes) + continue; + + foreach (var nested in GetTypes (type.NestedTypes)) + yield return nested; + } + } + + TypeDefinition GetNestedType (string fullname) + { + var names = fullname.Split ('/'); + var type = GetType (names [0]); + + if (type == null) + return null; + + for (int i = 1; i < names.Length; i++) { + var nested_type = type.GetNestedType (names [i]); + if (nested_type == null) + return null; + + type = nested_type; + } + + return type; + } + + internal FieldDefinition Resolve (FieldReference field) + { + return MetadataResolver.Resolve (field); + } + + internal MethodDefinition Resolve (MethodReference method) + { + return MetadataResolver.Resolve (method); + } + + internal TypeDefinition Resolve (TypeReference type) + { + return MetadataResolver.Resolve (type); + } + + static void CheckContext (IGenericParameterProvider context, ModuleDefinition module) + { + if (context == null) + return; + + if (context.Module != module) + throw new ArgumentException (); + } + + [Obsolete ("Use ImportReference", error: false)] + public TypeReference Import (Type type) + { + return ImportReference (type, null); + } + + public TypeReference ImportReference (Type type) + { + return ImportReference (type, null); + } + + [Obsolete ("Use ImportReference", error: false)] + public TypeReference Import (Type type, IGenericParameterProvider context) + { + return ImportReference (type, context); + } + + public TypeReference ImportReference (Type type, IGenericParameterProvider context) + { + Mixin.CheckType (type); + CheckContext (context, this); + + return ReflectionImporter.ImportReference (type, context); + } + + [Obsolete ("Use ImportReference", error: false)] + public FieldReference Import (SR.FieldInfo field) + { + return ImportReference (field, null); + } + + [Obsolete ("Use ImportReference", error: false)] + public FieldReference Import (SR.FieldInfo field, IGenericParameterProvider context) + { + return ImportReference (field, context); + } + + public FieldReference ImportReference (SR.FieldInfo field) + { + return ImportReference (field, null); + } + + public FieldReference ImportReference (SR.FieldInfo field, IGenericParameterProvider context) + { + Mixin.CheckField (field); + CheckContext (context, this); + + return ReflectionImporter.ImportReference (field, context); + } + + [Obsolete ("Use ImportReference", error: false)] + public MethodReference Import (SR.MethodBase method) + { + return ImportReference (method, null); + } + + [Obsolete ("Use ImportReference", error: false)] + public MethodReference Import (SR.MethodBase method, IGenericParameterProvider context) + { + return ImportReference (method, context); + } + + public MethodReference ImportReference (SR.MethodBase method) + { + return ImportReference (method, null); + } + + public MethodReference ImportReference (SR.MethodBase method, IGenericParameterProvider context) + { + Mixin.CheckMethod (method); + CheckContext (context, this); + + return ReflectionImporter.ImportReference (method, context); + } + + [Obsolete ("Use ImportReference", error: false)] + public TypeReference Import (TypeReference type) + { + return ImportReference (type, null); + } + + [Obsolete ("Use ImportReference", error: false)] + public TypeReference Import (TypeReference type, IGenericParameterProvider context) + { + return ImportReference (type, context); + } + + public TypeReference ImportReference (TypeReference type) + { + return ImportReference (type, null); + } + + public TypeReference ImportReference (TypeReference type, IGenericParameterProvider context) + { + Mixin.CheckType (type); + + if (type.Module == this) + return type; + + CheckContext (context, this); + + return MetadataImporter.ImportReference (type, context); + } + + [Obsolete ("Use ImportReference", error: false)] + public FieldReference Import (FieldReference field) + { + return ImportReference (field, null); + } + + [Obsolete ("Use ImportReference", error: false)] + public FieldReference Import (FieldReference field, IGenericParameterProvider context) + { + return ImportReference (field, context); + } + + public FieldReference ImportReference (FieldReference field) + { + return ImportReference (field, null); + } + + public FieldReference ImportReference (FieldReference field, IGenericParameterProvider context) + { + Mixin.CheckField (field); + + if (field.Module == this) + return field; + + CheckContext (context, this); + + return MetadataImporter.ImportReference (field, context); + } + + [Obsolete ("Use ImportReference", error: false)] + public MethodReference Import (MethodReference method) + { + return ImportReference (method, null); + } + + [Obsolete ("Use ImportReference", error: false)] + public MethodReference Import (MethodReference method, IGenericParameterProvider context) + { + return ImportReference (method, context); + } + + public MethodReference ImportReference (MethodReference method) + { + return ImportReference (method, null); + } + + public MethodReference ImportReference (MethodReference method, IGenericParameterProvider context) + { + Mixin.CheckMethod (method); + + if (method.Module == this) + return method; + + CheckContext (context, this); + + return MetadataImporter.ImportReference (method, context); + } + + public IMetadataTokenProvider LookupToken (int token) + { + return LookupToken (new MetadataToken ((uint)token)); + } + + public IMetadataTokenProvider LookupToken (MetadataToken token) + { + return Read (token, (t, reader) => reader.LookupToken (t)); + } + + public void ImmediateRead () + { + if (!HasImage) + return; + ReadingMode = ReadingMode.Immediate; + var moduleReader = new ImmediateModuleReader (Image); + moduleReader.ReadModule (this, resolve_attributes: true); + } + + readonly object module_lock = new object (); + + internal object SyncRoot { + get { return module_lock; } + } + + internal void Read (TItem item, Action read) + { + lock (module_lock) { + var position = reader.position; + var context = reader.context; + + read (item, reader); + + reader.position = position; + reader.context = context; + } + } + + internal TRet Read (TItem item, Func read) + { + lock (module_lock) { + var position = reader.position; + var context = reader.context; + + var ret = read (item, reader); + + reader.position = position; + reader.context = context; + + return ret; + } + } + + internal TRet Read (ref TRet variable, TItem item, Func read) where TRet : class + { + lock (module_lock) { + if (variable != null) + return variable; + + var position = reader.position; + var context = reader.context; + + var ret = read (item, reader); + + reader.position = position; + reader.context = context; + + return variable = ret; + } + } + + public bool HasDebugHeader { + get { return Image != null && Image.DebugHeader != null; } + } + + public ImageDebugHeader GetDebugHeader () + { + return Image.DebugHeader ?? new ImageDebugHeader (); + } + + public static ModuleDefinition CreateModule (string name, ModuleKind kind) + { + return CreateModule (name, new ModuleParameters { Kind = kind }); + } + + public static ModuleDefinition CreateModule (string name, ModuleParameters parameters) + { + Mixin.CheckName (name); + Mixin.CheckParameters (parameters); + + var module = new ModuleDefinition { + Name = name, + kind = parameters.Kind, + timestamp = parameters.Timestamp ?? Mixin.GetTimestamp (), + Runtime = parameters.Runtime, + architecture = parameters.Architecture, + mvid = Guid.NewGuid (), + Attributes = ModuleAttributes.ILOnly, + Characteristics = (ModuleCharacteristics)0x8540, + }; + + if (parameters.AssemblyResolver != null) + module.assembly_resolver = Disposable.NotOwned (parameters.AssemblyResolver); + + if (parameters.MetadataResolver != null) + module.metadata_resolver = parameters.MetadataResolver; + + if (parameters.MetadataImporterProvider != null) + module.metadata_importer = parameters.MetadataImporterProvider.GetMetadataImporter (module); + + if (parameters.ReflectionImporterProvider != null) + module.reflection_importer = parameters.ReflectionImporterProvider.GetReflectionImporter (module); + + if (parameters.Kind != ModuleKind.NetModule) { + var assembly = new AssemblyDefinition (); + module.assembly = assembly; + module.assembly.Name = CreateAssemblyName (name); + assembly.main_module = module; + } + + module.Types.Add (new TypeDefinition (string.Empty, "", TypeAttributes.NotPublic)); + + return module; + } + + static AssemblyNameDefinition CreateAssemblyName (string name) + { + if (name.EndsWith (".dll") || name.EndsWith (".exe")) + name = name.Substring (0, name.Length - 4); + + return new AssemblyNameDefinition (name, Mixin.ZeroVersion); + } + + public void ReadSymbols () + { + if (string.IsNullOrEmpty (file_name)) + throw new InvalidOperationException (); + + var provider = new DefaultSymbolReaderProvider (throwIfNoSymbol: true); + ReadSymbols (provider.GetSymbolReader (this, file_name), throwIfSymbolsAreNotMaching: true); + } + + public void ReadSymbols (ISymbolReader reader) + { + ReadSymbols (reader, throwIfSymbolsAreNotMaching: true); + } + + public void ReadSymbols (ISymbolReader reader, bool throwIfSymbolsAreNotMaching) + { + if (reader == null) + throw new ArgumentNullException ("reader"); + + symbol_reader = reader; + + if (!symbol_reader.ProcessDebugHeader (GetDebugHeader ())) { + symbol_reader = null; + + if (throwIfSymbolsAreNotMaching) + throw new SymbolsNotMatchingException ("Symbols were found but are not matching the assembly"); + + return; + } + + if (HasImage && ReadingMode == ReadingMode.Immediate) { + var immediate_reader = new ImmediateModuleReader (Image); + immediate_reader.ReadSymbols (this); + } + } + + public static ModuleDefinition ReadModule (string fileName) + { + return ReadModule (fileName, new ReaderParameters (ReadingMode.Deferred)); + } + + public static ModuleDefinition ReadModule (string fileName, ReaderParameters parameters) + { + var stream = GetFileStream (fileName, FileMode.Open, parameters.ReadWrite ? FileAccess.ReadWrite : FileAccess.Read, FileShare.Read); + + if (parameters.InMemory) { + var memory = new MemoryStream (stream.CanSeek ? (int)stream.Length : 0); + using (stream) + stream.CopyTo (memory); + + memory.Position = 0; + stream = memory; + } + + try { + return ReadModule (Disposable.Owned (stream), fileName, parameters); + } + catch (Exception) { + stream.Dispose (); + throw; + } + } + + static Stream GetFileStream (string fileName, FileMode mode, FileAccess access, FileShare share) + { + Mixin.CheckFileName (fileName); + + return new FileStream (fileName, mode, access, share); + } + + public static ModuleDefinition ReadModule (Stream stream) + { + return ReadModule (stream, new ReaderParameters (ReadingMode.Deferred)); + } + + public static ModuleDefinition ReadModule (Stream stream, ReaderParameters parameters) + { + Mixin.CheckStream (stream); + Mixin.CheckReadSeek (stream); + + return ReadModule (Disposable.NotOwned (stream), stream.GetFileName (), parameters); + } + + static ModuleDefinition ReadModule (Disposable stream, string fileName, ReaderParameters parameters) + { + Mixin.CheckParameters (parameters); + + return ModuleReader.CreateModule ( + ImageReader.ReadImage (stream, fileName), + parameters); + } + + public void Write (string fileName) + { + Write (fileName, new WriterParameters ()); + } + + public void Write (string fileName, WriterParameters parameters) + { + Mixin.CheckParameters (parameters); + var file = GetFileStream (fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.Read); + ModuleWriter.WriteModule (this, Disposable.Owned (file), parameters); + } + + public void Write () + { + Write (new WriterParameters ()); + } + + public void Write (WriterParameters parameters) + { + if (!HasImage) + throw new InvalidOperationException (); + + Write (Image.Stream.value, parameters); + } + + public void Write (Stream stream) + { + Write (stream, new WriterParameters ()); + } + + public void Write (Stream stream, WriterParameters parameters) + { + Mixin.CheckStream (stream); + Mixin.CheckWriteSeek (stream); + Mixin.CheckParameters (parameters); + + ModuleWriter.WriteModule (this, Disposable.NotOwned (stream), parameters); + } + } + + static partial class Mixin { + + public enum Argument { + name, + fileName, + fullName, + stream, + type, + method, + field, + parameters, + module, + modifierType, + eventType, + fieldType, + declaringType, + returnType, + propertyType, + interfaceType, + constraintType, + } + + public static void CheckName (object name) + { + if (name == null) + throw new ArgumentNullException (Argument.name.ToString ()); + } + + public static void CheckName (string name) + { + if (string.IsNullOrEmpty (name)) + throw new ArgumentNullOrEmptyException (Argument.name.ToString ()); + } + + public static void CheckFileName (string fileName) + { + if (string.IsNullOrEmpty (fileName)) + throw new ArgumentNullOrEmptyException (Argument.fileName.ToString ()); + } + + public static void CheckFullName (string fullName) + { + if (string.IsNullOrEmpty (fullName)) + throw new ArgumentNullOrEmptyException (Argument.fullName.ToString ()); + } + + public static void CheckStream (object stream) + { + if (stream == null) + throw new ArgumentNullException (Argument.stream.ToString ()); + } + + public static void CheckWriteSeek (Stream stream) + { + if (!stream.CanWrite || !stream.CanSeek) + throw new ArgumentException ("Stream must be writable and seekable."); + } + + public static void CheckReadSeek (Stream stream) + { + if (!stream.CanRead || !stream.CanSeek) + throw new ArgumentException ("Stream must be readable and seekable."); + } + + public static void CheckType (object type) + { + if (type == null) + throw new ArgumentNullException (Argument.type.ToString ()); + } + + public static void CheckType (object type, Argument argument) + { + if (type == null) + throw new ArgumentNullException (argument.ToString ()); + } + + public static void CheckField (object field) + { + if (field == null) + throw new ArgumentNullException (Argument.field.ToString ()); + } + + public static void CheckMethod (object method) + { + if (method == null) + throw new ArgumentNullException (Argument.method.ToString ()); + } + + public static void CheckParameters (object parameters) + { + if (parameters == null) + throw new ArgumentNullException (Argument.parameters.ToString ()); + } + + public static uint GetTimestamp () + { + return (uint)DateTime.UtcNow.Subtract (new DateTime (1970, 1, 1)).TotalSeconds; + } + + public static bool HasImage (this ModuleDefinition self) + { + return self != null && self.HasImage; + } + + public static string GetFileName (this Stream self) + { + var file_stream = self as FileStream; + if (file_stream == null) + return string.Empty; + + return Path.GetFullPath (file_stream.Name); + } + + public static TargetRuntime ParseRuntime (this string self) + { + if (string.IsNullOrEmpty (self)) + return TargetRuntime.Net_4_0; + + switch (self [1]) { + case '1': + return self [3] == '0' + ? TargetRuntime.Net_1_0 + : TargetRuntime.Net_1_1; + case '2': + return TargetRuntime.Net_2_0; + case '4': + default: + return TargetRuntime.Net_4_0; + } + } + + public static string RuntimeVersionString (this TargetRuntime runtime) + { + switch (runtime) { + case TargetRuntime.Net_1_0: + return "v1.0.3705"; + case TargetRuntime.Net_1_1: + return "v1.1.4322"; + case TargetRuntime.Net_2_0: + return "v2.0.50727"; + case TargetRuntime.Net_4_0: + default: + return "v4.0.30319"; + } + } + + public static bool IsWindowsMetadata (this ModuleDefinition module) + { + return module.MetadataKind != MetadataKind.Ecma335; + } + + public static byte [] ReadAll (this Stream self) + { + int read; + var memory = new MemoryStream ((int)self.Length); + var buffer = new byte [1024]; + + while ((read = self.Read (buffer, 0, buffer.Length)) != 0) + memory.Write (buffer, 0, read); + + return memory.ToArray (); + } + + public static void Read (object o) + { + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleDefinition.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleDefinition.cs.meta new file mode 100644 index 0000000..d34c5c1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleDefinition.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: be36668e876daba419d4cf3aa5e8cac9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleKind.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleKind.cs new file mode 100644 index 0000000..429fa8f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleKind.cs @@ -0,0 +1,55 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil { + + public enum ModuleKind { + Dll, + Console, + Windows, + NetModule, + } + + public enum MetadataKind { + Ecma335, + WindowsMetadata, + ManagedWindowsMetadata, + } + + public enum TargetArchitecture { + I386 = 0x014c, + AMD64 = 0x8664, + IA64 = 0x0200, + ARM = 0x01c0, + ARMv7 = 0x01c4, + ARM64 = 0xaa64, + } + + [Flags] + public enum ModuleAttributes { + ILOnly = 1, + Required32Bit = 2, + ILLibrary = 4, + StrongNameSigned = 8, + Preferred32Bit = 0x00020000, + } + + [Flags] + public enum ModuleCharacteristics { + HighEntropyVA = 0x0020, + DynamicBase = 0x0040, + NoSEH = 0x0400, + NXCompat = 0x0100, + AppContainer = 0x1000, + TerminalServerAware = 0x8000, + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleKind.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleKind.cs.meta new file mode 100644 index 0000000..110cc83 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleKind.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b85d54df2658b6d4bab3d27002f45a01 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleReference.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleReference.cs new file mode 100644 index 0000000..eb22872 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleReference.cs @@ -0,0 +1,49 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil { + + public class ModuleReference : IMetadataScope { + + string name; + + internal MetadataToken token; + + public string Name { + get { return name; } + set { name = value; } + } + + public virtual MetadataScopeType MetadataScopeType { + get { return MetadataScopeType.ModuleReference; } + } + + public MetadataToken MetadataToken { + get { return token; } + set { token = value; } + } + + internal ModuleReference () + { + this.token = new MetadataToken (TokenType.ModuleRef); + } + + public ModuleReference (string name) + : this () + { + this.name = name; + } + + public override string ToString () + { + return name; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleReference.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleReference.cs.meta new file mode 100644 index 0000000..3510c8c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleReference.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2b28049ba3ae6394dbe9f227aaf9c88e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/NativeType.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/NativeType.cs new file mode 100644 index 0000000..b2ed6d2 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/NativeType.cs @@ -0,0 +1,55 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil { + + public enum NativeType { + None = 0x66, + + Boolean = 0x02, + I1 = 0x03, + U1 = 0x04, + I2 = 0x05, + U2 = 0x06, + I4 = 0x07, + U4 = 0x08, + I8 = 0x09, + U8 = 0x0a, + R4 = 0x0b, + R8 = 0x0c, + LPStr = 0x14, + Int = 0x1f, + UInt = 0x20, + Func = 0x26, + Array = 0x2a, + + // Msft specific + Currency = 0x0f, + BStr = 0x13, + LPWStr = 0x15, + LPTStr = 0x16, + FixedSysString = 0x17, + IUnknown = 0x19, + IDispatch = 0x1a, + Struct = 0x1b, + IntF = 0x1c, + SafeArray = 0x1d, + FixedArray = 0x1e, + ByValStr = 0x22, + ANSIBStr = 0x23, + TBStr = 0x24, + VariantBool = 0x25, + ASAny = 0x28, + LPStruct = 0x2b, + CustomMarshaler = 0x2c, + Error = 0x2d, + Max = 0x50 + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/NativeType.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/NativeType.cs.meta new file mode 100644 index 0000000..a6dd298 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/NativeType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bb32c8b42f4f314448ff4b2c7eae0533 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PInvokeAttributes.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PInvokeAttributes.cs new file mode 100644 index 0000000..d99aca4 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PInvokeAttributes.cs @@ -0,0 +1,44 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil { + + [Flags] + public enum PInvokeAttributes : ushort { + NoMangle = 0x0001, // PInvoke is to use the member name as specified + + // Character set + CharSetMask = 0x0006, + CharSetNotSpec = 0x0000, + CharSetAnsi = 0x0002, + CharSetUnicode = 0x0004, + CharSetAuto = 0x0006, + + SupportsLastError = 0x0040, // Information about target function. Not relevant for fields + + // Calling convetion + CallConvMask = 0x0700, + CallConvWinapi = 0x0100, + CallConvCdecl = 0x0200, + CallConvStdCall = 0x0300, + CallConvThiscall = 0x0400, + CallConvFastcall = 0x0500, + + BestFitMask = 0x0030, + BestFitEnabled = 0x0010, + BestFitDisabled = 0x0020, + + ThrowOnUnmappableCharMask = 0x3000, + ThrowOnUnmappableCharEnabled = 0x1000, + ThrowOnUnmappableCharDisabled = 0x2000, + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PInvokeAttributes.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PInvokeAttributes.cs.meta new file mode 100644 index 0000000..e1bed46 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PInvokeAttributes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cf3019a5e2270654abca972684f5c2f9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PInvokeInfo.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PInvokeInfo.cs new file mode 100644 index 0000000..079c6bc --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PInvokeInfo.cs @@ -0,0 +1,120 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil { + + public sealed class PInvokeInfo { + + ushort attributes; + string entry_point; + ModuleReference module; + + public PInvokeAttributes Attributes { + get { return (PInvokeAttributes)attributes; } + set { attributes = (ushort)value; } + } + + public string EntryPoint { + get { return entry_point; } + set { entry_point = value; } + } + + public ModuleReference Module { + get { return module; } + set { module = value; } + } + + #region PInvokeAttributes + + public bool IsNoMangle { + get { return attributes.GetAttributes ((ushort)PInvokeAttributes.NoMangle); } + set { attributes = attributes.SetAttributes ((ushort)PInvokeAttributes.NoMangle, value); } + } + + public bool IsCharSetNotSpec { + get { return attributes.GetMaskedAttributes ((ushort)PInvokeAttributes.CharSetMask, (ushort)PInvokeAttributes.CharSetNotSpec); } + set { attributes = attributes.SetMaskedAttributes ((ushort)PInvokeAttributes.CharSetMask, (ushort)PInvokeAttributes.CharSetNotSpec, value); } + } + + public bool IsCharSetAnsi { + get { return attributes.GetMaskedAttributes ((ushort)PInvokeAttributes.CharSetMask, (ushort)PInvokeAttributes.CharSetAnsi); } + set { attributes = attributes.SetMaskedAttributes ((ushort)PInvokeAttributes.CharSetMask, (ushort)PInvokeAttributes.CharSetAnsi, value); } + } + + public bool IsCharSetUnicode { + get { return attributes.GetMaskedAttributes ((ushort)PInvokeAttributes.CharSetMask, (ushort)PInvokeAttributes.CharSetUnicode); } + set { attributes = attributes.SetMaskedAttributes ((ushort)PInvokeAttributes.CharSetMask, (ushort)PInvokeAttributes.CharSetUnicode, value); } + } + + public bool IsCharSetAuto { + get { return attributes.GetMaskedAttributes ((ushort)PInvokeAttributes.CharSetMask, (ushort)PInvokeAttributes.CharSetAuto); } + set { attributes = attributes.SetMaskedAttributes ((ushort)PInvokeAttributes.CharSetMask, (ushort)PInvokeAttributes.CharSetAuto, value); } + } + + public bool SupportsLastError { + get { return attributes.GetAttributes ((ushort)PInvokeAttributes.SupportsLastError); } + set { attributes = attributes.SetAttributes ((ushort)PInvokeAttributes.SupportsLastError, value); } + } + + public bool IsCallConvWinapi { + get { return attributes.GetMaskedAttributes ((ushort)PInvokeAttributes.CallConvMask, (ushort)PInvokeAttributes.CallConvWinapi); } + set { attributes = attributes.SetMaskedAttributes ((ushort)PInvokeAttributes.CallConvMask, (ushort)PInvokeAttributes.CallConvWinapi, value); } + } + + public bool IsCallConvCdecl { + get { return attributes.GetMaskedAttributes ((ushort)PInvokeAttributes.CallConvMask, (ushort)PInvokeAttributes.CallConvCdecl); } + set { attributes = attributes.SetMaskedAttributes ((ushort)PInvokeAttributes.CallConvMask, (ushort)PInvokeAttributes.CallConvCdecl, value); } + } + + public bool IsCallConvStdCall { + get { return attributes.GetMaskedAttributes ((ushort)PInvokeAttributes.CallConvMask, (ushort)PInvokeAttributes.CallConvStdCall); } + set { attributes = attributes.SetMaskedAttributes ((ushort)PInvokeAttributes.CallConvMask, (ushort)PInvokeAttributes.CallConvStdCall, value); } + } + + public bool IsCallConvThiscall { + get { return attributes.GetMaskedAttributes ((ushort)PInvokeAttributes.CallConvMask, (ushort)PInvokeAttributes.CallConvThiscall); } + set { attributes = attributes.SetMaskedAttributes ((ushort)PInvokeAttributes.CallConvMask, (ushort)PInvokeAttributes.CallConvThiscall, value); } + } + + public bool IsCallConvFastcall { + get { return attributes.GetMaskedAttributes ((ushort)PInvokeAttributes.CallConvMask, (ushort)PInvokeAttributes.CallConvFastcall); } + set { attributes = attributes.SetMaskedAttributes ((ushort)PInvokeAttributes.CallConvMask, (ushort)PInvokeAttributes.CallConvFastcall, value); } + } + + public bool IsBestFitEnabled { + get { return attributes.GetMaskedAttributes ((ushort)PInvokeAttributes.BestFitMask, (ushort)PInvokeAttributes.BestFitEnabled); } + set { attributes = attributes.SetMaskedAttributes ((ushort)PInvokeAttributes.BestFitMask, (ushort)PInvokeAttributes.BestFitEnabled, value); } + } + + public bool IsBestFitDisabled { + get { return attributes.GetMaskedAttributes ((ushort)PInvokeAttributes.BestFitMask, (ushort)PInvokeAttributes.BestFitDisabled); } + set { attributes = attributes.SetMaskedAttributes ((ushort)PInvokeAttributes.BestFitMask, (ushort)PInvokeAttributes.BestFitDisabled, value); } + } + + public bool IsThrowOnUnmappableCharEnabled { + get { return attributes.GetMaskedAttributes ((ushort)PInvokeAttributes.ThrowOnUnmappableCharMask, (ushort)PInvokeAttributes.ThrowOnUnmappableCharEnabled); } + set { attributes = attributes.SetMaskedAttributes ((ushort)PInvokeAttributes.ThrowOnUnmappableCharMask, (ushort)PInvokeAttributes.ThrowOnUnmappableCharEnabled, value); } + } + + public bool IsThrowOnUnmappableCharDisabled { + get { return attributes.GetMaskedAttributes ((ushort)PInvokeAttributes.ThrowOnUnmappableCharMask, (ushort)PInvokeAttributes.ThrowOnUnmappableCharDisabled); } + set { attributes = attributes.SetMaskedAttributes ((ushort)PInvokeAttributes.ThrowOnUnmappableCharMask, (ushort)PInvokeAttributes.ThrowOnUnmappableCharDisabled, value); } + } + + #endregion + + public PInvokeInfo (PInvokeAttributes attributes, string entryPoint, ModuleReference module) + { + this.attributes = (ushort)attributes; + this.entry_point = entryPoint; + this.module = module; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PInvokeInfo.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PInvokeInfo.cs.meta new file mode 100644 index 0000000..4c27d4b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PInvokeInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 56eb336b3d1ea2b4094fbb5586a11d49 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterAttributes.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterAttributes.cs new file mode 100644 index 0000000..387e1dc --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterAttributes.cs @@ -0,0 +1,27 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil { + + [Flags] + public enum ParameterAttributes : ushort { + None = 0x0000, + In = 0x0001, // Param is [In] + Out = 0x0002, // Param is [Out] + Lcid = 0x0004, + Retval = 0x0008, + Optional = 0x0010, // Param is optional + HasDefault = 0x1000, // Param has default value + HasFieldMarshal = 0x2000, // Param has field marshal + Unused = 0xcfe0 // Reserved: shall be zero in a conforming implementation + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterAttributes.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterAttributes.cs.meta new file mode 100644 index 0000000..487cc73 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterAttributes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dc153e8db6e9d1b4ca432cd66ecfe423 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterDefinition.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterDefinition.cs new file mode 100644 index 0000000..cd4905a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterDefinition.cs @@ -0,0 +1,146 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; + +namespace MonoFN.Cecil { + + public sealed class ParameterDefinition : ParameterReference, ICustomAttributeProvider, IConstantProvider, IMarshalInfoProvider { + + ushort attributes; + + internal IMethodSignature method; + + object constant = Mixin.NotResolved; + Collection custom_attributes; + MarshalInfo marshal_info; + + public ParameterAttributes Attributes { + get { return (ParameterAttributes)attributes; } + set { attributes = (ushort)value; } + } + + public IMethodSignature Method { + get { return method; } + } + + public int Sequence { + get { + if (method == null) + return -1; + + return method.HasImplicitThis () ? index + 1 : index; + } + } + + public bool HasConstant { + get { + this.ResolveConstant (ref constant, parameter_type.Module); + + return constant != Mixin.NoValue; + } + set { if (!value) constant = Mixin.NoValue; } + } + + public object Constant { + get { return HasConstant ? constant : null; } + set { constant = value; } + } + + public bool HasCustomAttributes { + get { + if (custom_attributes != null) + return custom_attributes.Count > 0; + + return this.GetHasCustomAttributes (parameter_type.Module); + } + } + + public Collection CustomAttributes { + get { return custom_attributes ?? (this.GetCustomAttributes (ref custom_attributes, parameter_type.Module)); } + } + + public bool HasMarshalInfo { + get { + if (marshal_info != null) + return true; + + return this.GetHasMarshalInfo (parameter_type.Module); + } + } + + public MarshalInfo MarshalInfo { + get { return marshal_info ?? (this.GetMarshalInfo (ref marshal_info, parameter_type.Module)); } + set { marshal_info = value; } + } + + #region ParameterAttributes + + public bool IsIn { + get { return attributes.GetAttributes ((ushort)ParameterAttributes.In); } + set { attributes = attributes.SetAttributes ((ushort)ParameterAttributes.In, value); } + } + + public bool IsOut { + get { return attributes.GetAttributes ((ushort)ParameterAttributes.Out); } + set { attributes = attributes.SetAttributes ((ushort)ParameterAttributes.Out, value); } + } + + public bool IsLcid { + get { return attributes.GetAttributes ((ushort)ParameterAttributes.Lcid); } + set { attributes = attributes.SetAttributes ((ushort)ParameterAttributes.Lcid, value); } + } + + public bool IsReturnValue { + get { return attributes.GetAttributes ((ushort)ParameterAttributes.Retval); } + set { attributes = attributes.SetAttributes ((ushort)ParameterAttributes.Retval, value); } + } + + public bool IsOptional { + get { return attributes.GetAttributes ((ushort)ParameterAttributes.Optional); } + set { attributes = attributes.SetAttributes ((ushort)ParameterAttributes.Optional, value); } + } + + public bool HasDefault { + get { return attributes.GetAttributes ((ushort)ParameterAttributes.HasDefault); } + set { attributes = attributes.SetAttributes ((ushort)ParameterAttributes.HasDefault, value); } + } + + public bool HasFieldMarshal { + get { return attributes.GetAttributes ((ushort)ParameterAttributes.HasFieldMarshal); } + set { attributes = attributes.SetAttributes ((ushort)ParameterAttributes.HasFieldMarshal, value); } + } + + #endregion + + internal ParameterDefinition (TypeReference parameterType, IMethodSignature method) + : this (string.Empty, ParameterAttributes.None, parameterType) + { + this.method = method; + } + + public ParameterDefinition (TypeReference parameterType) + : this (string.Empty, ParameterAttributes.None, parameterType) + { + } + + public ParameterDefinition (string name, ParameterAttributes attributes, TypeReference parameterType) + : base (name, parameterType) + { + this.attributes = (ushort)attributes; + this.token = new MetadataToken (TokenType.Param); + } + + public override ParameterDefinition Resolve () + { + return this; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterDefinition.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterDefinition.cs.meta new file mode 100644 index 0000000..06cef24 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterDefinition.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b6759139d8b53974087e609a88da6072 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterDefinitionCollection.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterDefinitionCollection.cs new file mode 100644 index 0000000..26eaec0 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterDefinitionCollection.cs @@ -0,0 +1,60 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; + +namespace MonoFN.Cecil { + + sealed class ParameterDefinitionCollection : Collection { + + readonly IMethodSignature method; + + internal ParameterDefinitionCollection (IMethodSignature method) + { + this.method = method; + } + + internal ParameterDefinitionCollection (IMethodSignature method, int capacity) + : base (capacity) + { + this.method = method; + } + + protected override void OnAdd (ParameterDefinition item, int index) + { + item.method = method; + item.index = index; + } + + protected override void OnInsert (ParameterDefinition item, int index) + { + item.method = method; + item.index = index; + + for (int i = index; i < size; i++) + items [i].index = i + 1; + } + + protected override void OnSet (ParameterDefinition item, int index) + { + item.method = method; + item.index = index; + } + + protected override void OnRemove (ParameterDefinition item, int index) + { + item.method = null; + item.index = -1; + + for (int i = index + 1; i < size; i++) + items [i].index = i - 1; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterDefinitionCollection.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterDefinitionCollection.cs.meta new file mode 100644 index 0000000..1e96811 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterDefinitionCollection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 01e788194a3a3604784e347ac41ec6b1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterReference.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterReference.cs new file mode 100644 index 0000000..b84fe3f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterReference.cs @@ -0,0 +1,57 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil { + + public abstract class ParameterReference : IMetadataTokenProvider { + + string name; + internal int index = -1; + protected TypeReference parameter_type; + internal MetadataToken token; + + public string Name { + get { return name; } + set { name = value; } + } + + public int Index { + get { return index; } + } + + public TypeReference ParameterType { + get { return parameter_type; } + set { parameter_type = value; } + } + + public MetadataToken MetadataToken { + get { return token; } + set { token = value; } + } + + internal ParameterReference (string name, TypeReference parameterType) + { + if (parameterType == null) + throw new ArgumentNullException ("parameterType"); + + this.name = name ?? string.Empty; + this.parameter_type = parameterType; + } + + public override string ToString () + { + return name; + } + + public abstract ParameterDefinition Resolve (); + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterReference.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterReference.cs.meta new file mode 100644 index 0000000..69166ca --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterReference.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0643ce0ca6e47bf4e92518aed8a3a955 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PinnedType.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PinnedType.cs new file mode 100644 index 0000000..dc83596 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PinnedType.cs @@ -0,0 +1,35 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +using MD = MonoFN.Cecil.Metadata; + +namespace MonoFN.Cecil { + + public sealed class PinnedType : TypeSpecification { + + public override bool IsValueType { + get { return false; } + set { throw new InvalidOperationException (); } + } + + public override bool IsPinned { + get { return true; } + } + + public PinnedType (TypeReference type) + : base (type) + { + Mixin.CheckType (type); + this.etype = MD.ElementType.Pinned; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PinnedType.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PinnedType.cs.meta new file mode 100644 index 0000000..325921b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PinnedType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6840e25b64ef0924d84724d5587cfa68 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PointerType.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PointerType.cs new file mode 100644 index 0000000..5c27093 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PointerType.cs @@ -0,0 +1,43 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +using MD = MonoFN.Cecil.Metadata; + +namespace MonoFN.Cecil { + + public sealed class PointerType : TypeSpecification { + + public override string Name { + get { return base.Name + "*"; } + } + + public override string FullName { + get { return base.FullName + "*"; } + } + + public override bool IsValueType { + get { return false; } + set { throw new InvalidOperationException (); } + } + + public override bool IsPointer { + get { return true; } + } + + public PointerType (TypeReference type) + : base (type) + { + Mixin.CheckType (type); + this.etype = MD.ElementType.Ptr; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PointerType.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PointerType.cs.meta new file mode 100644 index 0000000..665c696 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PointerType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7b6a1c540eedbb04082628ad5dd10583 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyAttributes.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyAttributes.cs new file mode 100644 index 0000000..1f32a51 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyAttributes.cs @@ -0,0 +1,23 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil { + + [Flags] + public enum PropertyAttributes : ushort { + None = 0x0000, + SpecialName = 0x0200, // Property is special + RTSpecialName = 0x0400, // Runtime(metadata internal APIs) should check name encoding + HasDefault = 0x1000, // Property has default + Unused = 0xe9ff // Reserved: shall be zero in a conforming implementation + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyAttributes.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyAttributes.cs.meta new file mode 100644 index 0000000..6a0dbc7 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyAttributes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 163bf99004c8e294f8b4113c29cae987 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyDefinition.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyDefinition.cs new file mode 100644 index 0000000..3268ffd --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyDefinition.cs @@ -0,0 +1,245 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System.Text; +using System.Threading; + +namespace MonoFN.Cecil { + + public sealed class PropertyDefinition : PropertyReference, IMemberDefinition, IConstantProvider { + + bool? has_this; + ushort attributes; + + Collection custom_attributes; + + internal MethodDefinition get_method; + internal MethodDefinition set_method; + internal Collection other_methods; + + object constant = Mixin.NotResolved; + + public PropertyAttributes Attributes { + get { return (PropertyAttributes)attributes; } + set { attributes = (ushort)value; } + } + + public bool HasThis { + get { + if (has_this.HasValue) + return has_this.Value; + + if (GetMethod != null) + return get_method.HasThis; + + if (SetMethod != null) + return set_method.HasThis; + + return false; + } + set { has_this = value; } + } + + public bool HasCustomAttributes { + get { + if (custom_attributes != null) + return custom_attributes.Count > 0; + + return this.GetHasCustomAttributes (Module); + } + } + + public Collection CustomAttributes { + get { return custom_attributes ?? (this.GetCustomAttributes (ref custom_attributes, Module)); } + } + + public MethodDefinition GetMethod { + get { + if (get_method != null) + return get_method; + + InitializeMethods (); + return get_method; + } + set { get_method = value; } + } + + public MethodDefinition SetMethod { + get { + if (set_method != null) + return set_method; + + InitializeMethods (); + return set_method; + } + set { set_method = value; } + } + + public bool HasOtherMethods { + get { + if (other_methods != null) + return other_methods.Count > 0; + + InitializeMethods (); + return !other_methods.IsNullOrEmpty (); + } + } + + public Collection OtherMethods { + get { + if (other_methods != null) + return other_methods; + + InitializeMethods (); + + if (other_methods != null) + return other_methods; + + Interlocked.CompareExchange (ref other_methods, new Collection (), null); + return other_methods; + } + } + + public bool HasParameters { + get { + InitializeMethods (); + + if (get_method != null) + return get_method.HasParameters; + + if (set_method != null) + return set_method.HasParameters && set_method.Parameters.Count > 1; + + return false; + } + } + + public override Collection Parameters { + get { + InitializeMethods (); + + if (get_method != null) + return MirrorParameters (get_method, 0); + + if (set_method != null) + return MirrorParameters (set_method, 1); + + return new Collection (); + } + } + + static Collection MirrorParameters (MethodDefinition method, int bound) + { + var parameters = new Collection (); + if (!method.HasParameters) + return parameters; + + var original_parameters = method.Parameters; + var end = original_parameters.Count - bound; + + for (int i = 0; i < end; i++) + parameters.Add (original_parameters [i]); + + return parameters; + } + + public bool HasConstant { + get { + this.ResolveConstant (ref constant, Module); + + return constant != Mixin.NoValue; + } + set { if (!value) constant = Mixin.NoValue; } + } + + public object Constant { + get { return HasConstant ? constant : null; } + set { constant = value; } + } + + #region PropertyAttributes + + public bool IsSpecialName { + get { return attributes.GetAttributes ((ushort)PropertyAttributes.SpecialName); } + set { attributes = attributes.SetAttributes ((ushort)PropertyAttributes.SpecialName, value); } + } + + public bool IsRuntimeSpecialName { + get { return attributes.GetAttributes ((ushort)PropertyAttributes.RTSpecialName); } + set { attributes = attributes.SetAttributes ((ushort)PropertyAttributes.RTSpecialName, value); } + } + + public bool HasDefault { + get { return attributes.GetAttributes ((ushort)PropertyAttributes.HasDefault); } + set { attributes = attributes.SetAttributes ((ushort)PropertyAttributes.HasDefault, value); } + } + + #endregion + + public new TypeDefinition DeclaringType { + get { return (TypeDefinition)base.DeclaringType; } + set { base.DeclaringType = value; } + } + + public override bool IsDefinition { + get { return true; } + } + + public override string FullName { + get { + var builder = new StringBuilder (); + builder.Append (PropertyType.ToString ()); + builder.Append (' '); + builder.Append (MemberFullName ()); + builder.Append ('('); + if (HasParameters) { + var parameters = Parameters; + for (int i = 0; i < parameters.Count; i++) { + if (i > 0) + builder.Append (','); + builder.Append (parameters [i].ParameterType.FullName); + } + } + builder.Append (')'); + return builder.ToString (); + } + } + + public PropertyDefinition (string name, PropertyAttributes attributes, TypeReference propertyType) + : base (name, propertyType) + { + this.attributes = (ushort)attributes; + this.token = new MetadataToken (TokenType.Property); + } + + void InitializeMethods () + { + var module = this.Module; + if (module == null) + return; + + lock (module.SyncRoot) { + if (get_method != null || set_method != null) + return; + + if (!module.HasImage ()) + return; + + module.Read (this, (property, reader) => reader.ReadMethods (property)); + } + } + + public override PropertyDefinition Resolve () + { + return this; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyDefinition.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyDefinition.cs.meta new file mode 100644 index 0000000..a372850 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyDefinition.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6c1878ca62c0e6f4595238eb6a87edfd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyReference.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyReference.cs new file mode 100644 index 0000000..d3f7a4c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyReference.cs @@ -0,0 +1,43 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; + +namespace MonoFN.Cecil { + + public abstract class PropertyReference : MemberReference { + + TypeReference property_type; + + public TypeReference PropertyType { + get { return property_type; } + set { property_type = value; } + } + + public abstract Collection Parameters { + get; + } + + internal PropertyReference (string name, TypeReference propertyType) + : base (name) + { + Mixin.CheckType (propertyType, Mixin.Argument.propertyType); + + property_type = propertyType; + } + + protected override IMemberDefinition ResolveDefinition () + { + return this.Resolve (); + } + + public new abstract PropertyDefinition Resolve (); + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyReference.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyReference.cs.meta new file mode 100644 index 0000000..a218f3c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyReference.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a30c5751a554ce649bf88a597e4075e9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ReferenceType.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ReferenceType.cs new file mode 100644 index 0000000..e2703a4 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ReferenceType.cs @@ -0,0 +1,43 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +using MD = MonoFN.Cecil.Metadata; + +namespace MonoFN.Cecil { + + public sealed class ByReferenceType : TypeSpecification { + + public override string Name { + get { return base.Name + "&"; } + } + + public override string FullName { + get { return base.FullName + "&"; } + } + + public override bool IsValueType { + get { return false; } + set { throw new InvalidOperationException (); } + } + + public override bool IsByReference { + get { return true; } + } + + public ByReferenceType (TypeReference type) + : base (type) + { + Mixin.CheckType (type); + this.etype = MD.ElementType.ByRef; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ReferenceType.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ReferenceType.cs.meta new file mode 100644 index 0000000..de5a90f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ReferenceType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 32a78d7552e92774cbf44ddd46fb7aa4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Resource.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Resource.cs new file mode 100644 index 0000000..92c007f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Resource.cs @@ -0,0 +1,58 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil { + + public enum ResourceType { + Linked, + Embedded, + AssemblyLinked, + } + + public abstract class Resource { + + string name; + uint attributes; + + public string Name { + get { return name; } + set { name = value; } + } + + public ManifestResourceAttributes Attributes { + get { return (ManifestResourceAttributes)attributes; } + set { attributes = (uint)value; } + } + + public abstract ResourceType ResourceType { + get; + } + + #region ManifestResourceAttributes + + public bool IsPublic { + get { return attributes.GetMaskedAttributes ((uint)ManifestResourceAttributes.VisibilityMask, (uint)ManifestResourceAttributes.Public); } + set { attributes = attributes.SetMaskedAttributes ((uint)ManifestResourceAttributes.VisibilityMask, (uint)ManifestResourceAttributes.Public, value); } + } + + public bool IsPrivate { + get { return attributes.GetMaskedAttributes ((uint)ManifestResourceAttributes.VisibilityMask, (uint)ManifestResourceAttributes.Private); } + set { attributes = attributes.SetMaskedAttributes ((uint)ManifestResourceAttributes.VisibilityMask, (uint)ManifestResourceAttributes.Private, value); } + } + + #endregion + + internal Resource (string name, ManifestResourceAttributes attributes) + { + this.name = name; + this.attributes = (uint)attributes; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Resource.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Resource.cs.meta new file mode 100644 index 0000000..c4c82c9 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Resource.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b8b738634202e694ebec627727698b83 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/SecurityDeclaration.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/SecurityDeclaration.cs new file mode 100644 index 0000000..c3e27b4 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/SecurityDeclaration.cs @@ -0,0 +1,201 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System; +using System.Diagnostics; +using System.Threading; + +namespace MonoFN.Cecil { + + public enum SecurityAction : ushort { + Request = 1, + Demand = 2, + Assert = 3, + Deny = 4, + PermitOnly = 5, + LinkDemand = 6, + InheritDemand = 7, + RequestMinimum = 8, + RequestOptional = 9, + RequestRefuse = 10, + PreJitGrant = 11, + PreJitDeny = 12, + NonCasDemand = 13, + NonCasLinkDemand = 14, + NonCasInheritance = 15 + } + + public interface ISecurityDeclarationProvider : IMetadataTokenProvider { + + bool HasSecurityDeclarations { get; } + Collection SecurityDeclarations { get; } + } + + [DebuggerDisplay ("{AttributeType}")] + public sealed class SecurityAttribute : ICustomAttribute { + + TypeReference attribute_type; + + internal Collection fields; + internal Collection properties; + + public TypeReference AttributeType { + get { return attribute_type; } + set { attribute_type = value; } + } + + public bool HasFields { + get { return !fields.IsNullOrEmpty (); } + } + + public Collection Fields { + get { + if (fields == null) + Interlocked.CompareExchange (ref fields, new Collection (), null); + + return fields; + } + } + + public bool HasProperties { + get { return !properties.IsNullOrEmpty (); } + } + + public Collection Properties { + get { + if (properties == null) + Interlocked.CompareExchange (ref properties, new Collection (), null); + + return properties; + } + } + + public SecurityAttribute (TypeReference attributeType) + { + this.attribute_type = attributeType; + } + + bool ICustomAttribute.HasConstructorArguments { + get { return false; } + } + + Collection ICustomAttribute.ConstructorArguments { + get { throw new NotSupportedException (); } + } + } + + public sealed class SecurityDeclaration { + + readonly internal uint signature; + byte [] blob; + readonly ModuleDefinition module; + + internal bool resolved; + SecurityAction action; + internal Collection security_attributes; + + public SecurityAction Action { + get { return action; } + set { action = value; } + } + + public bool HasSecurityAttributes { + get { + Resolve (); + + return !security_attributes.IsNullOrEmpty (); + } + } + + public Collection SecurityAttributes { + get { + Resolve (); + + if (security_attributes == null) + Interlocked.CompareExchange (ref security_attributes, new Collection (), null); + + return security_attributes; + } + } + + internal bool HasImage { + get { return module != null && module.HasImage; } + } + + internal SecurityDeclaration (SecurityAction action, uint signature, ModuleDefinition module) + { + this.action = action; + this.signature = signature; + this.module = module; + } + + public SecurityDeclaration (SecurityAction action) + { + this.action = action; + this.resolved = true; + } + + public SecurityDeclaration (SecurityAction action, byte [] blob) + { + this.action = action; + this.resolved = false; + this.blob = blob; + } + + public byte [] GetBlob () + { + if (blob != null) + return blob; + + if (!HasImage || signature == 0) + throw new NotSupportedException (); + + return module.Read (ref blob, this, (declaration, reader) => reader.ReadSecurityDeclarationBlob (declaration.signature)); + } + + void Resolve () + { + if (resolved || !HasImage) + return; + + lock (module.SyncRoot) { + + if (resolved) + return; + + module.Read (this, (declaration, reader) => reader.ReadSecurityDeclarationSignature (declaration)); + resolved = true; + } + } + } + + static partial class Mixin { + + public static bool GetHasSecurityDeclarations ( + this ISecurityDeclarationProvider self, + ModuleDefinition module) + { + return module.HasImage () && module.Read (self, (provider, reader) => reader.HasSecurityDeclarations (provider)); + } + + public static Collection GetSecurityDeclarations ( + this ISecurityDeclarationProvider self, + ref Collection variable, + ModuleDefinition module) + { + if (module.HasImage) + return module.Read (ref variable, self, (provider, reader) => reader.ReadSecurityDeclarations (provider)); + + Interlocked.CompareExchange (ref variable, new Collection (), null); + return variable; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/SecurityDeclaration.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/SecurityDeclaration.cs.meta new file mode 100644 index 0000000..67a6b92 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/SecurityDeclaration.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7a39c3df615f71b409a5aa3d102fb35e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/SentinelType.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/SentinelType.cs new file mode 100644 index 0000000..24e3840 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/SentinelType.cs @@ -0,0 +1,35 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +using MD = MonoFN.Cecil.Metadata; + +namespace MonoFN.Cecil { + + public sealed class SentinelType : TypeSpecification { + + public override bool IsValueType { + get { return false; } + set { throw new InvalidOperationException (); } + } + + public override bool IsSentinel { + get { return true; } + } + + public SentinelType (TypeReference type) + : base (type) + { + Mixin.CheckType (type); + this.etype = MD.ElementType.Sentinel; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/SentinelType.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/SentinelType.cs.meta new file mode 100644 index 0000000..f025abf --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/SentinelType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 31522df8b7c72c1499a3fb951e73a69a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TargetRuntime.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TargetRuntime.cs new file mode 100644 index 0000000..d2c48f3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TargetRuntime.cs @@ -0,0 +1,19 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil { + + public enum TargetRuntime { + Net_1_0, + Net_1_1, + Net_2_0, + Net_4_0, + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TargetRuntime.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TargetRuntime.cs.meta new file mode 100644 index 0000000..9192b08 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TargetRuntime.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 64ac90055dca01347b6ed93dff5e4284 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Treatments.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Treatments.cs new file mode 100644 index 0000000..4b65d6a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Treatments.cs @@ -0,0 +1,61 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil { + + [Flags] + enum TypeDefinitionTreatment { + None = 0x0, + + KindMask = 0xf, + NormalType = 0x1, + NormalAttribute = 0x2, + UnmangleWindowsRuntimeName = 0x3, + PrefixWindowsRuntimeName = 0x4, + RedirectToClrType = 0x5, + RedirectToClrAttribute = 0x6, + RedirectImplementedMethods = 0x7, + + Abstract = 0x10, + Internal = 0x20, + } + + enum TypeReferenceTreatment { + None = 0x0, + SystemDelegate = 0x1, + SystemAttribute = 0x2, + UseProjectionInfo = 0x3, + } + + [Flags] + enum MethodDefinitionTreatment { + None = 0x0, + Abstract = 0x2, + Private = 0x4, + Public = 0x8, + Runtime = 0x10, + InternalCall = 0x20, + } + + enum FieldDefinitionTreatment { + None = 0x0, + Public = 0x1, + } + + enum CustomAttributeValueTreatment { + None = 0x0, + AllowSingle = 0x1, + AllowMultiple = 0x2, + VersionAttribute = 0x3, + DeprecatedAttribute = 0x4, + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Treatments.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Treatments.cs.meta new file mode 100644 index 0000000..d9698b7 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Treatments.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d2bba6ea16ba63147b7bff91a65ad5fa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeAttributes.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeAttributes.cs new file mode 100644 index 0000000..dae187b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeAttributes.cs @@ -0,0 +1,63 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil { + + [Flags] + public enum TypeAttributes : uint { + // Visibility attributes + VisibilityMask = 0x00000007, // Use this mask to retrieve visibility information + NotPublic = 0x00000000, // Class has no public scope + Public = 0x00000001, // Class has public scope + NestedPublic = 0x00000002, // Class is nested with public visibility + NestedPrivate = 0x00000003, // Class is nested with private visibility + NestedFamily = 0x00000004, // Class is nested with family visibility + NestedAssembly = 0x00000005, // Class is nested with assembly visibility + NestedFamANDAssem = 0x00000006, // Class is nested with family and assembly visibility + NestedFamORAssem = 0x00000007, // Class is nested with family or assembly visibility + + // Class layout attributes + LayoutMask = 0x00000018, // Use this mask to retrieve class layout information + AutoLayout = 0x00000000, // Class fields are auto-laid out + SequentialLayout = 0x00000008, // Class fields are laid out sequentially + ExplicitLayout = 0x00000010, // Layout is supplied explicitly + + // Class semantics attributes + ClassSemanticMask = 0x00000020, // Use this mask to retrieve class semantics information + Class = 0x00000000, // Type is a class + Interface = 0x00000020, // Type is an interface + + // Special semantics in addition to class semantics + Abstract = 0x00000080, // Class is abstract + Sealed = 0x00000100, // Class cannot be extended + SpecialName = 0x00000400, // Class name is special + + // Implementation attributes + Import = 0x00001000, // Class/Interface is imported + Serializable = 0x00002000, // Class is serializable + WindowsRuntime = 0x00004000, // Windows Runtime type + + // String formatting attributes + StringFormatMask = 0x00030000, // Use this mask to retrieve string information for native interop + AnsiClass = 0x00000000, // LPSTR is interpreted as ANSI + UnicodeClass = 0x00010000, // LPSTR is interpreted as Unicode + AutoClass = 0x00020000, // LPSTR is interpreted automatically + + // Class initialization attributes + BeforeFieldInit = 0x00100000, // Initialize the class before first static field access + + // Additional flags + RTSpecialName = 0x00000800, // CLI provides 'special' behavior, depending upon the name of the Type + HasSecurity = 0x00040000, // Type has security associate with it + Forwarder = 0x00200000, // Exported type is a type forwarder + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeAttributes.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeAttributes.cs.meta new file mode 100644 index 0000000..eb01e16 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeAttributes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a634580522059d74bb5ad1f997af1284 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeComparisonMode.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeComparisonMode.cs new file mode 100644 index 0000000..b61e8aa --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeComparisonMode.cs @@ -0,0 +1,11 @@ +namespace MonoFN.Cecil { + internal enum TypeComparisonMode { + Exact, + SignatureOnly, + + /// + /// Types can be in different assemblies, as long as the module, assembly, and type names match they will be considered equal + /// + SignatureOnlyLoose + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeComparisonMode.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeComparisonMode.cs.meta new file mode 100644 index 0000000..235e452 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeComparisonMode.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3777ff31cb318a8488a68226f53f7381 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeDefinition.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeDefinition.cs new file mode 100644 index 0000000..6562ecc --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeDefinition.cs @@ -0,0 +1,618 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.Metadata; +using MonoFN.Collections.Generic; +using System; +using System.Threading; + +namespace MonoFN.Cecil { + + public sealed class TypeDefinition : TypeReference, IMemberDefinition, ISecurityDeclarationProvider { + + uint attributes; + TypeReference base_type; + internal Range fields_range; + internal Range methods_range; + + short packing_size = Mixin.NotResolvedMarker; + int class_size = Mixin.NotResolvedMarker; + + InterfaceImplementationCollection interfaces; + Collection nested_types; + Collection methods; + Collection fields; + Collection events; + Collection properties; + Collection custom_attributes; + Collection security_declarations; + + public TypeAttributes Attributes { + get { return (TypeAttributes)attributes; } + set { + if (IsWindowsRuntimeProjection && (ushort)value != attributes) + throw new InvalidOperationException (); + + attributes = (uint)value; + } + } + + public TypeReference BaseType { + get { return base_type; } + set { base_type = value; } + } + + public override string Name { + get { return base.Name; } + set { + if (IsWindowsRuntimeProjection && value != base.Name) + throw new InvalidOperationException (); + + base.Name = value; + } + } + + void ResolveLayout () + { + if (!HasImage) { + packing_size = Mixin.NoDataMarker; + class_size = Mixin.NoDataMarker; + return; + } + + lock (Module.SyncRoot) { + if (packing_size != Mixin.NotResolvedMarker || class_size != Mixin.NotResolvedMarker) + return; + + var row = Module.Read (this, (type, reader) => reader.ReadTypeLayout (type)); + + packing_size = row.Col1; + class_size = row.Col2; + } + } + + public bool HasLayoutInfo { + get { + if (packing_size >= 0 || class_size >= 0) + return true; + + ResolveLayout (); + + return packing_size >= 0 || class_size >= 0; + } + } + + public short PackingSize { + get { + if (packing_size >= 0) + return packing_size; + + ResolveLayout (); + + return packing_size >= 0 ? packing_size : (short)-1; + } + set { packing_size = value; } + } + + public int ClassSize { + get { + if (class_size >= 0) + return class_size; + + ResolveLayout (); + + return class_size >= 0 ? class_size : -1; + } + set { class_size = value; } + } + + public bool HasInterfaces { + get { + if (interfaces != null) + return interfaces.Count > 0; + + return HasImage && Module.Read (this, (type, reader) => reader.HasInterfaces (type)); + } + } + + public Collection Interfaces { + get { + if (interfaces != null) + return interfaces; + + if (HasImage) + return Module.Read (ref interfaces, this, (type, reader) => reader.ReadInterfaces (type)); + + Interlocked.CompareExchange (ref interfaces, new InterfaceImplementationCollection (this), null); + return interfaces; + } + } + + public bool HasNestedTypes { + get { + if (nested_types != null) + return nested_types.Count > 0; + + return HasImage && Module.Read (this, (type, reader) => reader.HasNestedTypes (type)); + } + } + + public Collection NestedTypes { + get { + if (nested_types != null) + return nested_types; + + if (HasImage) + return Module.Read (ref nested_types, this, (type, reader) => reader.ReadNestedTypes (type)); + + Interlocked.CompareExchange (ref nested_types, new MemberDefinitionCollection (this), null); + return nested_types; + } + } + + public bool HasMethods { + get { + if (methods != null) + return methods.Count > 0; + + return HasImage && methods_range.Length > 0; + } + } + + public Collection Methods { + get { + if (methods != null) + return methods; + + if (HasImage) + return Module.Read (ref methods, this, (type, reader) => reader.ReadMethods (type)); + + Interlocked.CompareExchange (ref methods, new MemberDefinitionCollection (this), null); + return methods; + } + } + + public bool HasFields { + get { + if (fields != null) + return fields.Count > 0; + + return HasImage && fields_range.Length > 0; + } + } + + public Collection Fields { + get { + if (fields != null) + return fields; + + if (HasImage) + return Module.Read (ref fields, this, (type, reader) => reader.ReadFields (type)); + + Interlocked.CompareExchange (ref fields, new MemberDefinitionCollection (this), null); + return fields; + } + } + + public bool HasEvents { + get { + if (events != null) + return events.Count > 0; + + return HasImage && Module.Read (this, (type, reader) => reader.HasEvents (type)); + } + } + + public Collection Events { + get { + if (events != null) + return events; + + if (HasImage) + return Module.Read (ref events, this, (type, reader) => reader.ReadEvents (type)); + + Interlocked.CompareExchange (ref events, new MemberDefinitionCollection (this), null); + return events; + } + } + + public bool HasProperties { + get { + if (properties != null) + return properties.Count > 0; + + return HasImage && Module.Read (this, (type, reader) => reader.HasProperties (type)); + } + } + + public Collection Properties { + get { + if (properties != null) + return properties; + + if (HasImage) + return Module.Read (ref properties, this, (type, reader) => reader.ReadProperties (type)); + + Interlocked.CompareExchange (ref properties, new MemberDefinitionCollection (this), null); + return properties; + } + } + + public bool HasSecurityDeclarations { + get { + if (security_declarations != null) + return security_declarations.Count > 0; + + return this.GetHasSecurityDeclarations (Module); + } + } + + public Collection SecurityDeclarations { + get { return security_declarations ?? (this.GetSecurityDeclarations (ref security_declarations, Module)); } + } + + public bool HasCustomAttributes { + get { + if (custom_attributes != null) + return custom_attributes.Count > 0; + + return this.GetHasCustomAttributes (Module); + } + } + + public Collection CustomAttributes { + get { return custom_attributes ?? (this.GetCustomAttributes (ref custom_attributes, Module)); } + } + + public override bool HasGenericParameters { + get { + if (generic_parameters != null) + return generic_parameters.Count > 0; + + return this.GetHasGenericParameters (Module); + } + } + + public override Collection GenericParameters { + get { return generic_parameters ?? (this.GetGenericParameters (ref generic_parameters, Module)); } + } + + #region TypeAttributes + + public bool IsNotPublic { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NotPublic); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NotPublic, value); } + } + + public bool IsPublic { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.Public); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.Public, value); } + } + + public bool IsNestedPublic { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedPublic); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedPublic, value); } + } + + public bool IsNestedPrivate { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedPrivate); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedPrivate, value); } + } + + public bool IsNestedFamily { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedFamily); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedFamily, value); } + } + + public bool IsNestedAssembly { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedAssembly); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedAssembly, value); } + } + + public bool IsNestedFamilyAndAssembly { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedFamANDAssem); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedFamANDAssem, value); } + } + + public bool IsNestedFamilyOrAssembly { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedFamORAssem); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.VisibilityMask, (uint)TypeAttributes.NestedFamORAssem, value); } + } + + public bool IsAutoLayout { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.LayoutMask, (uint)TypeAttributes.AutoLayout); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.LayoutMask, (uint)TypeAttributes.AutoLayout, value); } + } + + public bool IsSequentialLayout { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.LayoutMask, (uint)TypeAttributes.SequentialLayout); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.LayoutMask, (uint)TypeAttributes.SequentialLayout, value); } + } + + public bool IsExplicitLayout { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.LayoutMask, (uint)TypeAttributes.ExplicitLayout); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.LayoutMask, (uint)TypeAttributes.ExplicitLayout, value); } + } + + public bool IsClass { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.ClassSemanticMask, (uint)TypeAttributes.Class); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.ClassSemanticMask, (uint)TypeAttributes.Class, value); } + } + + public bool IsInterface { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.ClassSemanticMask, (uint)TypeAttributes.Interface); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.ClassSemanticMask, (uint)TypeAttributes.Interface, value); } + } + + public bool IsAbstract { + get { return attributes.GetAttributes ((uint)TypeAttributes.Abstract); } + set { attributes = attributes.SetAttributes ((uint)TypeAttributes.Abstract, value); } + } + + public bool IsSealed { + get { return attributes.GetAttributes ((uint)TypeAttributes.Sealed); } + set { attributes = attributes.SetAttributes ((uint)TypeAttributes.Sealed, value); } + } + + public bool IsSpecialName { + get { return attributes.GetAttributes ((uint)TypeAttributes.SpecialName); } + set { attributes = attributes.SetAttributes ((uint)TypeAttributes.SpecialName, value); } + } + + public bool IsImport { + get { return attributes.GetAttributes ((uint)TypeAttributes.Import); } + set { attributes = attributes.SetAttributes ((uint)TypeAttributes.Import, value); } + } + + public bool IsSerializable { + get { return attributes.GetAttributes ((uint)TypeAttributes.Serializable); } + set { attributes = attributes.SetAttributes ((uint)TypeAttributes.Serializable, value); } + } + + public bool IsWindowsRuntime { + get { return attributes.GetAttributes ((uint)TypeAttributes.WindowsRuntime); } + set { attributes = attributes.SetAttributes ((uint)TypeAttributes.WindowsRuntime, value); } + } + + public bool IsAnsiClass { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.StringFormatMask, (uint)TypeAttributes.AnsiClass); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.StringFormatMask, (uint)TypeAttributes.AnsiClass, value); } + } + + public bool IsUnicodeClass { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.StringFormatMask, (uint)TypeAttributes.UnicodeClass); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.StringFormatMask, (uint)TypeAttributes.UnicodeClass, value); } + } + + public bool IsAutoClass { + get { return attributes.GetMaskedAttributes ((uint)TypeAttributes.StringFormatMask, (uint)TypeAttributes.AutoClass); } + set { attributes = attributes.SetMaskedAttributes ((uint)TypeAttributes.StringFormatMask, (uint)TypeAttributes.AutoClass, value); } + } + + public bool IsBeforeFieldInit { + get { return attributes.GetAttributes ((uint)TypeAttributes.BeforeFieldInit); } + set { attributes = attributes.SetAttributes ((uint)TypeAttributes.BeforeFieldInit, value); } + } + + public bool IsRuntimeSpecialName { + get { return attributes.GetAttributes ((uint)TypeAttributes.RTSpecialName); } + set { attributes = attributes.SetAttributes ((uint)TypeAttributes.RTSpecialName, value); } + } + + public bool HasSecurity { + get { return attributes.GetAttributes ((uint)TypeAttributes.HasSecurity); } + set { attributes = attributes.SetAttributes ((uint)TypeAttributes.HasSecurity, value); } + } + + #endregion + + public bool IsEnum { + get { return base_type != null && base_type.IsTypeOf ("System", "Enum"); } + } + + public override bool IsValueType { + get { + if (base_type == null) + return false; + + return base_type.IsTypeOf ("System", "Enum") || (base_type.IsTypeOf ("System", "ValueType") && !this.IsTypeOf ("System", "Enum")); + } + set { + throw new NotSupportedException (); + } + } + + public override bool IsPrimitive { + get { + ElementType primitive_etype; + return MetadataSystem.TryGetPrimitiveElementType (this, out primitive_etype) && primitive_etype.IsPrimitive (); + } + } + + public override MetadataType MetadataType { + get { + ElementType primitive_etype; + if (MetadataSystem.TryGetPrimitiveElementType (this, out primitive_etype)) + return (MetadataType)primitive_etype; + + return base.MetadataType; + } + } + + public override bool IsDefinition { + get { return true; } + } + + public new TypeDefinition DeclaringType { + get { return (TypeDefinition)base.DeclaringType; } + set { base.DeclaringType = value; } + } + + internal new TypeDefinitionProjection WindowsRuntimeProjection { + get { return (TypeDefinitionProjection)projection; } + set { projection = value; } + } + + public TypeDefinition (string @namespace, string name, TypeAttributes attributes) + : base (@namespace, name) + { + this.attributes = (uint)attributes; + this.token = new MetadataToken (TokenType.TypeDef); + } + + public TypeDefinition (string @namespace, string name, TypeAttributes attributes, TypeReference baseType) : + this (@namespace, name, attributes) + { + this.BaseType = baseType; + } + + protected override void ClearFullName () + { + base.ClearFullName (); + + if (!HasNestedTypes) + return; + + var nested_types = this.NestedTypes; + + for (int i = 0; i < nested_types.Count; i++) + nested_types [i].ClearFullName (); + } + + public override TypeDefinition Resolve () + { + return this; + } + } + + public sealed class InterfaceImplementation : ICustomAttributeProvider { + internal TypeDefinition type; + internal MetadataToken token; + + TypeReference interface_type; + Collection custom_attributes; + + public TypeReference InterfaceType { + get { return interface_type; } + set { interface_type = value; } + } + + public bool HasCustomAttributes { + get { + if (custom_attributes != null) + return custom_attributes.Count > 0; + + if (type == null) + return false; + + return this.GetHasCustomAttributes (type.Module); + } + } + + public Collection CustomAttributes { + get { + if (type == null) { + if (custom_attributes == null) + Interlocked.CompareExchange (ref custom_attributes, new Collection (), null); + return custom_attributes; + } + + return custom_attributes ?? (this.GetCustomAttributes (ref custom_attributes, type.Module)); + } + } + + public MetadataToken MetadataToken { + get { return token; } + set { token = value; } + } + + internal InterfaceImplementation (TypeReference interfaceType, MetadataToken token) + { + this.interface_type = interfaceType; + this.token = token; + } + + public InterfaceImplementation (TypeReference interfaceType) + { + Mixin.CheckType (interfaceType, Mixin.Argument.interfaceType); + + this.interface_type = interfaceType; + this.token = new MetadataToken (TokenType.InterfaceImpl); + } + } + + class InterfaceImplementationCollection : Collection { + readonly TypeDefinition type; + + internal InterfaceImplementationCollection (TypeDefinition type) + { + this.type = type; + } + + internal InterfaceImplementationCollection (TypeDefinition type, int length) + : base (length) + { + this.type = type; + } + + protected override void OnAdd (InterfaceImplementation item, int index) + { + item.type = type; + } + + protected override void OnInsert (InterfaceImplementation item, int index) + { + item.type = type; + } + + protected override void OnSet (InterfaceImplementation item, int index) + { + item.type = type; + } + + protected override void OnRemove (InterfaceImplementation item, int index) + { + item.type = null; + } + } + + static partial class Mixin { + + public static TypeReference GetEnumUnderlyingType (this TypeDefinition self) + { + var fields = self.Fields; + + for (int i = 0; i < fields.Count; i++) { + var field = fields [i]; + if (!field.IsStatic) + return field.FieldType; + } + + throw new ArgumentException (); + } + + public static TypeDefinition GetNestedType (this TypeDefinition self, string fullname) + { + if (!self.HasNestedTypes) + return null; + + var nested_types = self.NestedTypes; + + for (int i = 0; i < nested_types.Count; i++) { + var nested_type = nested_types [i]; + + if (nested_type.TypeFullName () == fullname) + return nested_type; + } + + return null; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeDefinition.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeDefinition.cs.meta new file mode 100644 index 0000000..c55bd55 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeDefinition.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4616a4ea6dc219f419ae9ba98109f98c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeDefinitionCollection.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeDefinitionCollection.cs new file mode 100644 index 0000000..aa07389 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeDefinitionCollection.cs @@ -0,0 +1,98 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.Metadata; +using MonoFN.Collections.Generic; +using System; +using System.Collections.Generic; + +namespace MonoFN.Cecil { + + using Slot = Row; + + sealed class TypeDefinitionCollection : Collection { + + readonly ModuleDefinition container; + readonly Dictionary name_cache; + + internal TypeDefinitionCollection (ModuleDefinition container) + { + this.container = container; + this.name_cache = new Dictionary (new RowEqualityComparer ()); + } + + internal TypeDefinitionCollection (ModuleDefinition container, int capacity) + : base (capacity) + { + this.container = container; + this.name_cache = new Dictionary (capacity, new RowEqualityComparer ()); + } + + protected override void OnAdd (TypeDefinition item, int index) + { + Attach (item); + } + + protected override void OnSet (TypeDefinition item, int index) + { + Attach (item); + } + + protected override void OnInsert (TypeDefinition item, int index) + { + Attach (item); + } + + protected override void OnRemove (TypeDefinition item, int index) + { + Detach (item); + } + + protected override void OnClear () + { + foreach (var type in this) + Detach (type); + } + + void Attach (TypeDefinition type) + { + if (type.Module != null && type.Module != container) + throw new ArgumentException ("Type already attached"); + + type.module = container; + type.scope = container; + name_cache [new Slot (type.Namespace, type.Name)] = type; + } + + void Detach (TypeDefinition type) + { + type.module = null; + type.scope = null; + name_cache.Remove (new Slot (type.Namespace, type.Name)); + } + + public TypeDefinition GetType (string fullname) + { + string @namespace, name; + TypeParser.SplitFullName (fullname, out @namespace, out name); + + return GetType (@namespace, name); + } + + public TypeDefinition GetType (string @namespace, string name) + { + TypeDefinition type; + if (name_cache.TryGetValue (new Slot (@namespace, name), out type)) + return type; + + return null; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeDefinitionCollection.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeDefinitionCollection.cs.meta new file mode 100644 index 0000000..e800734 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeDefinitionCollection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4440ec34ba0da7b4aac0fc4c009035aa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeParser.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeParser.cs new file mode 100644 index 0000000..c533b98 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeParser.cs @@ -0,0 +1,531 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.Metadata; +using System; +using System.Text; + +namespace MonoFN.Cecil { + + class TypeParser { + + class Type { + public const int Ptr = -1; + public const int ByRef = -2; + public const int SzArray = -3; + + public string type_fullname; + public string [] nested_names; + public int arity; + public int [] specs; + public Type [] generic_arguments; + public string assembly; + } + + readonly string fullname; + readonly int length; + + int position; + + TypeParser (string fullname) + { + this.fullname = fullname; + this.length = fullname.Length; + } + + Type ParseType (bool fq_name) + { + var type = new Type (); + type.type_fullname = ParsePart (); + + type.nested_names = ParseNestedNames (); + + if (TryGetArity (type)) + type.generic_arguments = ParseGenericArguments (type.arity); + + type.specs = ParseSpecs (); + + if (fq_name) + type.assembly = ParseAssemblyName (); + + return type; + } + + static bool TryGetArity (Type type) + { + int arity = 0; + + TryAddArity (type.type_fullname, ref arity); + + var nested_names = type.nested_names; + if (!nested_names.IsNullOrEmpty ()) { + for (int i = 0; i < nested_names.Length; i++) + TryAddArity (nested_names [i], ref arity); + } + + type.arity = arity; + return arity > 0; + } + + static bool TryGetArity (string name, out int arity) + { + arity = 0; + var index = name.LastIndexOf ('`'); + if (index == -1) + return false; + + return ParseInt32 (name.Substring (index + 1), out arity); + } + + static bool ParseInt32 (string value, out int result) + { + return int.TryParse (value, out result); + } + + static void TryAddArity (string name, ref int arity) + { + int type_arity; + if (!TryGetArity (name, out type_arity)) + return; + + arity += type_arity; + } + + string ParsePart () + { + var part = new StringBuilder (); + while (position < length && !IsDelimiter (fullname [position])) { + if (fullname [position] == '\\') + position++; + + part.Append (fullname [position++]); + } + + return part.ToString (); + } + + static bool IsDelimiter (char chr) + { + return "+,[]*&".IndexOf (chr) != -1; + } + + void TryParseWhiteSpace () + { + while (position < length && Char.IsWhiteSpace (fullname [position])) + position++; + } + + string [] ParseNestedNames () + { + string [] nested_names = null; + while (TryParse ('+')) + Add (ref nested_names, ParsePart ()); + + return nested_names; + } + + bool TryParse (char chr) + { + if (position < length && fullname [position] == chr) { + position++; + return true; + } + + return false; + } + + static void Add (ref T [] array, T item) + { + array = array.Add (item); + } + + int [] ParseSpecs () + { + int [] specs = null; + + while (position < length) { + switch (fullname [position]) { + case '*': + position++; + Add (ref specs, Type.Ptr); + break; + case '&': + position++; + Add (ref specs, Type.ByRef); + break; + case '[': + position++; + switch (fullname [position]) { + case ']': + position++; + Add (ref specs, Type.SzArray); + break; + case '*': + position++; + Add (ref specs, 1); + break; + default: + var rank = 1; + while (TryParse (',')) + rank++; + + Add (ref specs, rank); + + TryParse (']'); + break; + } + break; + default: + return specs; + } + } + + return specs; + } + + Type [] ParseGenericArguments (int arity) + { + Type [] generic_arguments = null; + + if (position == length || fullname [position] != '[') + return generic_arguments; + + TryParse ('['); + + for (int i = 0; i < arity; i++) { + var fq_argument = TryParse ('['); + Add (ref generic_arguments, ParseType (fq_argument)); + if (fq_argument) + TryParse (']'); + + TryParse (','); + TryParseWhiteSpace (); + } + + TryParse (']'); + + return generic_arguments; + } + + string ParseAssemblyName () + { + if (!TryParse (',')) + return string.Empty; + + TryParseWhiteSpace (); + + var start = position; + while (position < length) { + var chr = fullname [position]; + if (chr == '[' || chr == ']') + break; + + position++; + } + + return fullname.Substring (start, position - start); + } + + public static TypeReference ParseType (ModuleDefinition module, string fullname, bool typeDefinitionOnly = false) + { + if (string.IsNullOrEmpty (fullname)) + return null; + + var parser = new TypeParser (fullname); + return GetTypeReference (module, parser.ParseType (true), typeDefinitionOnly); + } + + static TypeReference GetTypeReference (ModuleDefinition module, Type type_info, bool type_def_only) + { + TypeReference type; + if (!TryGetDefinition (module, type_info, out type)) { + if (type_def_only) + return null; + + type = CreateReference (type_info, module, GetMetadataScope (module, type_info)); + } + + return CreateSpecs (type, type_info); + } + + static TypeReference CreateSpecs (TypeReference type, Type type_info) + { + type = TryCreateGenericInstanceType (type, type_info); + + var specs = type_info.specs; + if (specs.IsNullOrEmpty ()) + return type; + + for (int i = 0; i < specs.Length; i++) { + switch (specs [i]) { + case Type.Ptr: + type = new PointerType (type); + break; + case Type.ByRef: + type = new ByReferenceType (type); + break; + case Type.SzArray: + type = new ArrayType (type); + break; + default: + var array = new ArrayType (type); + array.Dimensions.Clear (); + + for (int j = 0; j < specs [i]; j++) + array.Dimensions.Add (new ArrayDimension ()); + + type = array; + break; + } + } + + return type; + } + + static TypeReference TryCreateGenericInstanceType (TypeReference type, Type type_info) + { + var generic_arguments = type_info.generic_arguments; + if (generic_arguments.IsNullOrEmpty ()) + return type; + + var instance = new GenericInstanceType (type, generic_arguments.Length); + var instance_arguments = instance.GenericArguments; + + for (int i = 0; i < generic_arguments.Length; i++) + instance_arguments.Add (GetTypeReference (type.Module, generic_arguments [i], false)); + + return instance; + } + + public static void SplitFullName (string fullname, out string @namespace, out string name) + { + var last_dot = fullname.LastIndexOf ('.'); + + if (last_dot == -1) { + @namespace = string.Empty; + name = fullname; + } else { + @namespace = fullname.Substring (0, last_dot); + name = fullname.Substring (last_dot + 1); + } + } + + static TypeReference CreateReference (Type type_info, ModuleDefinition module, IMetadataScope scope) + { + string @namespace, name; + SplitFullName (type_info.type_fullname, out @namespace, out name); + + var type = new TypeReference (@namespace, name, module, scope); + MetadataSystem.TryProcessPrimitiveTypeReference (type); + + AdjustGenericParameters (type); + + var nested_names = type_info.nested_names; + if (nested_names.IsNullOrEmpty ()) + return type; + + for (int i = 0; i < nested_names.Length; i++) { + type = new TypeReference (string.Empty, nested_names [i], module, null) { + DeclaringType = type, + }; + + AdjustGenericParameters (type); + } + + return type; + } + + static void AdjustGenericParameters (TypeReference type) + { + int arity; + if (!TryGetArity (type.Name, out arity)) + return; + + for (int i = 0; i < arity; i++) + type.GenericParameters.Add (new GenericParameter (type)); + } + + static IMetadataScope GetMetadataScope (ModuleDefinition module, Type type_info) + { + if (string.IsNullOrEmpty (type_info.assembly)) + return module.TypeSystem.CoreLibrary; + + AssemblyNameReference match; + var reference = AssemblyNameReference.Parse (type_info.assembly); + + return module.TryGetAssemblyNameReference (reference, out match) + ? match + : reference; + } + + static bool TryGetDefinition (ModuleDefinition module, Type type_info, out TypeReference type) + { + type = null; + if (!TryCurrentModule (module, type_info)) + return false; + + var typedef = module.GetType (type_info.type_fullname); + if (typedef == null) + return false; + + var nested_names = type_info.nested_names; + if (!nested_names.IsNullOrEmpty ()) { + for (int i = 0; i < nested_names.Length; i++) { + var nested_type = typedef.GetNestedType (nested_names [i]); + if (nested_type == null) + return false; + + typedef = nested_type; + } + } + + type = typedef; + return true; + } + + static bool TryCurrentModule (ModuleDefinition module, Type type_info) + { + if (string.IsNullOrEmpty (type_info.assembly)) + return true; + + if (module.assembly != null && module.assembly.Name.FullName == type_info.assembly) + return true; + + return false; + } + + public static string ToParseable (TypeReference type, bool top_level = true) + { + if (type == null) + return null; + + var name = new StringBuilder (); + AppendType (type, name, true, top_level); + return name.ToString (); + } + + static void AppendNamePart (string part, StringBuilder name) + { + foreach (var c in part) { + if (IsDelimiter (c)) + name.Append ('\\'); + + name.Append (c); + } + } + + static void AppendType (TypeReference type, StringBuilder name, bool fq_name, bool top_level) + { + var element_type = type.GetElementType (); + + var declaring_type = element_type.DeclaringType; + if (declaring_type != null) { + AppendType (declaring_type, name, false, top_level); + name.Append ('+'); + } + + var @namespace = type.Namespace; + if (!string.IsNullOrEmpty (@namespace)) { + AppendNamePart (@namespace, name); + name.Append ('.'); + } + + AppendNamePart (element_type.Name, name); + + if (!fq_name) + return; + + if (type.IsTypeSpecification ()) + AppendTypeSpecification ((TypeSpecification)type, name); + + if (RequiresFullyQualifiedName (type, top_level)) { + name.Append (", "); + name.Append (GetScopeFullName (type)); + } + } + + static string GetScopeFullName (TypeReference type) + { + var scope = type.Scope; + switch (scope.MetadataScopeType) { + case MetadataScopeType.AssemblyNameReference: + return ((AssemblyNameReference)scope).FullName; + case MetadataScopeType.ModuleDefinition: + return ((ModuleDefinition)scope).Assembly.Name.FullName; + } + + throw new ArgumentException (); + } + + static void AppendTypeSpecification (TypeSpecification type, StringBuilder name) + { + if (type.ElementType.IsTypeSpecification ()) + AppendTypeSpecification ((TypeSpecification)type.ElementType, name); + + switch (type.etype) { + case ElementType.Ptr: + name.Append ('*'); + break; + case ElementType.ByRef: + name.Append ('&'); + break; + case ElementType.SzArray: + case ElementType.Array: + var array = (ArrayType)type; + if (array.IsVector) { + name.Append ("[]"); + } else { + name.Append ('['); + for (int i = 1; i < array.Rank; i++) + name.Append (','); + name.Append (']'); + } + break; + case ElementType.GenericInst: + var instance = (GenericInstanceType)type; + var arguments = instance.GenericArguments; + + name.Append ('['); + + for (int i = 0; i < arguments.Count; i++) { + if (i > 0) + name.Append (','); + + var argument = arguments [i]; + var requires_fqname = argument.Scope != argument.Module; + + if (requires_fqname) + name.Append ('['); + + AppendType (argument, name, true, false); + + if (requires_fqname) + name.Append (']'); + } + + name.Append (']'); + break; + default: + return; + } + } + + static bool RequiresFullyQualifiedName (TypeReference type, bool top_level) + { + if (type.Scope == type.Module) + return false; + + if (type.Scope.Name == "mscorlib" && top_level) + return false; + + return true; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeParser.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeParser.cs.meta new file mode 100644 index 0000000..0cc685b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeParser.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 14cea45910a5fc94ca89429bd0587148 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeReference.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeReference.cs new file mode 100644 index 0000000..993daf5 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeReference.cs @@ -0,0 +1,352 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.Metadata; +using MonoFN.Collections.Generic; +using System; +using System.Threading; + +namespace MonoFN.Cecil { + + public enum MetadataType : byte { + Void = ElementType.Void, + Boolean = ElementType.Boolean, + Char = ElementType.Char, + SByte = ElementType.I1, + Byte = ElementType.U1, + Int16 = ElementType.I2, + UInt16 = ElementType.U2, + Int32 = ElementType.I4, + UInt32 = ElementType.U4, + Int64 = ElementType.I8, + UInt64 = ElementType.U8, + Single = ElementType.R4, + Double = ElementType.R8, + String = ElementType.String, + Pointer = ElementType.Ptr, + ByReference = ElementType.ByRef, + ValueType = ElementType.ValueType, + Class = ElementType.Class, + Var = ElementType.Var, + Array = ElementType.Array, + GenericInstance = ElementType.GenericInst, + TypedByReference = ElementType.TypedByRef, + IntPtr = ElementType.I, + UIntPtr = ElementType.U, + FunctionPointer = ElementType.FnPtr, + Object = ElementType.Object, + MVar = ElementType.MVar, + RequiredModifier = ElementType.CModReqD, + OptionalModifier = ElementType.CModOpt, + Sentinel = ElementType.Sentinel, + Pinned = ElementType.Pinned, + } + + public class TypeReference : MemberReference, IGenericParameterProvider, IGenericContext { + + string @namespace; + bool value_type; + internal IMetadataScope scope; + internal ModuleDefinition module; + + internal ElementType etype = ElementType.None; + + string fullname; + + protected Collection generic_parameters; + + public override string Name { + get { return base.Name; } + set { + if (IsWindowsRuntimeProjection && value != base.Name) + throw new InvalidOperationException ("Projected type reference name can't be changed."); + base.Name = value; + ClearFullName (); + } + } + + public virtual string Namespace { + get { return @namespace; } + set { + if (IsWindowsRuntimeProjection && value != @namespace) + throw new InvalidOperationException ("Projected type reference namespace can't be changed."); + @namespace = value; + ClearFullName (); + } + } + + public virtual bool IsValueType { + get { return value_type; } + set { value_type = value; } + } + + public override ModuleDefinition Module { + get { + if (module != null) + return module; + + var declaring_type = this.DeclaringType; + if (declaring_type != null) + return declaring_type.Module; + + return null; + } + } + + internal TypeReferenceProjection WindowsRuntimeProjection { + get { return (TypeReferenceProjection)projection; } + set { projection = value; } + } + + IGenericParameterProvider IGenericContext.Type { + get { return this; } + } + + IGenericParameterProvider IGenericContext.Method { + get { return null; } + } + + GenericParameterType IGenericParameterProvider.GenericParameterType { + get { return GenericParameterType.Type; } + } + + public virtual bool HasGenericParameters { + get { return !generic_parameters.IsNullOrEmpty (); } + } + + public virtual Collection GenericParameters { + get { + if (generic_parameters == null) + Interlocked.CompareExchange (ref generic_parameters, new GenericParameterCollection (this), null); + + return generic_parameters; + } + } + + public virtual IMetadataScope Scope { + get { + var declaring_type = this.DeclaringType; + if (declaring_type != null) + return declaring_type.Scope; + + return scope; + } + set { + var declaring_type = this.DeclaringType; + if (declaring_type != null) { + if (IsWindowsRuntimeProjection && value != declaring_type.Scope) + throw new InvalidOperationException ("Projected type scope can't be changed."); + declaring_type.Scope = value; + return; + } + + if (IsWindowsRuntimeProjection && value != scope) + throw new InvalidOperationException ("Projected type scope can't be changed."); + scope = value; + } + } + + public bool IsNested { + get { return this.DeclaringType != null; } + } + + public override TypeReference DeclaringType { + get { return base.DeclaringType; } + set { + if (IsWindowsRuntimeProjection && value != base.DeclaringType) + throw new InvalidOperationException ("Projected type declaring type can't be changed."); + base.DeclaringType = value; + ClearFullName (); + } + } + + public override string FullName { + get { + if (fullname != null) + return fullname; + + var new_fullname = this.TypeFullName (); + + if (IsNested) + new_fullname = DeclaringType.FullName + "/" + new_fullname; + Interlocked.CompareExchange (ref fullname, new_fullname, null); + return fullname; + } + } + + public virtual bool IsByReference { + get { return false; } + } + + public virtual bool IsPointer { + get { return false; } + } + + public virtual bool IsSentinel { + get { return false; } + } + + public virtual bool IsArray { + get { return false; } + } + + public virtual bool IsGenericParameter { + get { return false; } + } + + public virtual bool IsGenericInstance { + get { return false; } + } + + public virtual bool IsRequiredModifier { + get { return false; } + } + + public virtual bool IsOptionalModifier { + get { return false; } + } + + public virtual bool IsPinned { + get { return false; } + } + + public virtual bool IsFunctionPointer { + get { return false; } + } + + public virtual bool IsPrimitive { + get { return etype.IsPrimitive (); } + } + + public virtual MetadataType MetadataType { + get { + switch (etype) { + case ElementType.None: + return IsValueType ? MetadataType.ValueType : MetadataType.Class; + default: + return (MetadataType)etype; + } + } + } + + protected TypeReference (string @namespace, string name) + : base (name) + { + this.@namespace = @namespace ?? string.Empty; + this.token = new MetadataToken (TokenType.TypeRef, 0); + } + + public TypeReference (string @namespace, string name, ModuleDefinition module, IMetadataScope scope) + : this (@namespace, name) + { + this.module = module; + this.scope = scope; + } + + public TypeReference (string @namespace, string name, ModuleDefinition module, IMetadataScope scope, bool valueType) : + this (@namespace, name, module, scope) + { + value_type = valueType; + } + + protected virtual void ClearFullName () + { + this.fullname = null; + } + + public virtual TypeReference GetElementType () + { + return this; + } + + protected override IMemberDefinition ResolveDefinition () + { + return this.Resolve (); + } + + public new virtual TypeDefinition Resolve () + { + var module = this.Module; + if (module == null) + throw new NotSupportedException (); + + return module.Resolve (this); + } + } + + static partial class Mixin { + + public static bool IsPrimitive (this ElementType self) + { + switch (self) { + case ElementType.Boolean: + case ElementType.Char: + case ElementType.I: + case ElementType.U: + case ElementType.I1: + case ElementType.U1: + case ElementType.I2: + case ElementType.U2: + case ElementType.I4: + case ElementType.U4: + case ElementType.I8: + case ElementType.U8: + case ElementType.R4: + case ElementType.R8: + return true; + default: + return false; + } + } + + public static string TypeFullName (this TypeReference self) + { + return string.IsNullOrEmpty (self.Namespace) + ? self.Name + : self.Namespace + '.' + self.Name; + } + + public static bool IsTypeOf (this TypeReference self, string @namespace, string name) + { + return self.Name == name + && self.Namespace == @namespace; + } + + public static bool IsTypeSpecification (this TypeReference type) + { + switch (type.etype) { + case ElementType.Array: + case ElementType.ByRef: + case ElementType.CModOpt: + case ElementType.CModReqD: + case ElementType.FnPtr: + case ElementType.GenericInst: + case ElementType.MVar: + case ElementType.Pinned: + case ElementType.Ptr: + case ElementType.SzArray: + case ElementType.Sentinel: + case ElementType.Var: + return true; + } + + return false; + } + + public static TypeDefinition CheckedResolve (this TypeReference self) + { + var type = self.Resolve (); + if (type == null) + throw new ResolutionException (self); + + return type; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeReference.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeReference.cs.meta new file mode 100644 index 0000000..6133f4f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeReference.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: be8b0c2aa3d8d534a90ff392789fac67 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeReferenceEqualityComparer.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeReferenceEqualityComparer.cs new file mode 100644 index 0000000..a399fba --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeReferenceEqualityComparer.cs @@ -0,0 +1,253 @@ +using System; +using System.Collections.Generic; + +namespace MonoFN.Cecil { + internal sealed class TypeReferenceEqualityComparer : EqualityComparer { + public override bool Equals (TypeReference x, TypeReference y) + { + return AreEqual (x, y); + } + + public override int GetHashCode (TypeReference obj) + { + return GetHashCodeFor (obj); + } + + public static bool AreEqual (TypeReference a, TypeReference b, TypeComparisonMode comparisonMode = TypeComparisonMode.Exact) + { + if (ReferenceEquals (a, b)) + return true; + + if (a == null || b == null) + return false; + + var aMetadataType = a.MetadataType; + var bMetadataType = b.MetadataType; + + if (aMetadataType == MetadataType.GenericInstance || bMetadataType == MetadataType.GenericInstance) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual ((GenericInstanceType)a, (GenericInstanceType)b, comparisonMode); + } + + if (aMetadataType == MetadataType.Array || bMetadataType == MetadataType.Array) { + if (aMetadataType != bMetadataType) + return false; + + var a1 = (ArrayType)a; + var b1 = (ArrayType)b; + if (a1.Rank != b1.Rank) + return false; + + return AreEqual (a1.ElementType, b1.ElementType, comparisonMode); + } + + if (aMetadataType == MetadataType.Var || bMetadataType == MetadataType.Var) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual ((GenericParameter)a, (GenericParameter)b, comparisonMode); + } + + if (aMetadataType == MetadataType.MVar || bMetadataType == MetadataType.MVar) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual ((GenericParameter)a, (GenericParameter)b, comparisonMode); + } + + if (aMetadataType == MetadataType.ByReference || bMetadataType == MetadataType.ByReference) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual (((ByReferenceType)a).ElementType, ((ByReferenceType)b).ElementType, comparisonMode); + } + + if (aMetadataType == MetadataType.Pointer || bMetadataType == MetadataType.Pointer) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual (((PointerType)a).ElementType, ((PointerType)b).ElementType, comparisonMode); + } + + if (aMetadataType == MetadataType.RequiredModifier || bMetadataType == MetadataType.RequiredModifier) { + if (aMetadataType != bMetadataType) + return false; + + var a1 = (RequiredModifierType)a; + var b1 = (RequiredModifierType)b; + + return AreEqual (a1.ModifierType, b1.ModifierType, comparisonMode) && AreEqual (a1.ElementType, b1.ElementType, comparisonMode); + } + + if (aMetadataType == MetadataType.OptionalModifier || bMetadataType == MetadataType.OptionalModifier) { + if (aMetadataType != bMetadataType) + return false; + + var a1 = (OptionalModifierType)a; + var b1 = (OptionalModifierType)b; + + return AreEqual (a1.ModifierType, b1.ModifierType, comparisonMode) && AreEqual (a1.ElementType, b1.ElementType, comparisonMode); + } + + if (aMetadataType == MetadataType.Pinned || bMetadataType == MetadataType.Pinned) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual (((PinnedType)a).ElementType, ((PinnedType)b).ElementType, comparisonMode); + } + + if (aMetadataType == MetadataType.Sentinel || bMetadataType == MetadataType.Sentinel) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual (((SentinelType)a).ElementType, ((SentinelType)b).ElementType, comparisonMode); + } + + if (!a.Name.Equals (b.Name) || !a.Namespace.Equals (b.Namespace)) + return false; + + var xDefinition = a.Resolve (); + var yDefinition = b.Resolve (); + + // For loose signature the types could be in different assemblies, as long as the type names match we will consider them equal + if (comparisonMode == TypeComparisonMode.SignatureOnlyLoose) { + if (xDefinition.Module.Name != yDefinition.Module.Name) + return false; + + if (xDefinition.Module.Assembly.Name.Name != yDefinition.Module.Assembly.Name.Name) + return false; + + return xDefinition.FullName == yDefinition.FullName; + } + + return xDefinition == yDefinition; + } + + static bool AreEqual (GenericParameter a, GenericParameter b, TypeComparisonMode comparisonMode = TypeComparisonMode.Exact) + { + if (ReferenceEquals (a, b)) + return true; + + if (a.Position != b.Position) + return false; + + if (a.Type != b.Type) + return false; + + var aOwnerType = a.Owner as TypeReference; + if (aOwnerType != null && AreEqual (aOwnerType, b.Owner as TypeReference, comparisonMode)) + return true; + + var aOwnerMethod = a.Owner as MethodReference; + if (aOwnerMethod != null && comparisonMode != TypeComparisonMode.SignatureOnlyLoose && MethodReferenceComparer.AreEqual (aOwnerMethod, b.Owner as MethodReference)) + return true; + + return comparisonMode == TypeComparisonMode.SignatureOnly || comparisonMode == TypeComparisonMode.SignatureOnlyLoose; + } + + static bool AreEqual (GenericInstanceType a, GenericInstanceType b, TypeComparisonMode comparisonMode = TypeComparisonMode.Exact) + { + if (ReferenceEquals (a, b)) + return true; + + var aGenericArgumentsCount = a.GenericArguments.Count; + if (aGenericArgumentsCount != b.GenericArguments.Count) + return false; + + if (!AreEqual (a.ElementType, b.ElementType, comparisonMode)) + return false; + + for (int i = 0; i < aGenericArgumentsCount; i++) + if (!AreEqual (a.GenericArguments [i], b.GenericArguments [i], comparisonMode)) + return false; + + return true; + } + + public static int GetHashCodeFor (TypeReference obj) + { + // a very good prime number + const int hashCodeMultiplier = 486187739; + // prime numbers + const int genericInstanceTypeMultiplier = 31; + const int byReferenceMultiplier = 37; + const int pointerMultiplier = 41; + const int requiredModifierMultiplier = 43; + const int optionalModifierMultiplier = 47; + const int pinnedMultiplier = 53; + const int sentinelMultiplier = 59; + + var metadataType = obj.MetadataType; + + if (metadataType == MetadataType.GenericInstance) { + var genericInstanceType = (GenericInstanceType)obj; + var hashCode = GetHashCodeFor (genericInstanceType.ElementType) * hashCodeMultiplier + genericInstanceTypeMultiplier; + for (var i = 0; i < genericInstanceType.GenericArguments.Count; i++) + hashCode = hashCode * hashCodeMultiplier + GetHashCodeFor (genericInstanceType.GenericArguments [i]); + return hashCode; + } + + if (metadataType == MetadataType.Array) { + var arrayType = (ArrayType)obj; + return GetHashCodeFor (arrayType.ElementType) * hashCodeMultiplier + arrayType.Rank.GetHashCode (); + } + + if (metadataType == MetadataType.Var || metadataType == MetadataType.MVar) { + var genericParameter = (GenericParameter)obj; + var hashCode = genericParameter.Position.GetHashCode () * hashCodeMultiplier + ((int)metadataType).GetHashCode (); + + var ownerTypeReference = genericParameter.Owner as TypeReference; + if (ownerTypeReference != null) + return hashCode * hashCodeMultiplier + GetHashCodeFor (ownerTypeReference); + + var ownerMethodReference = genericParameter.Owner as MethodReference; + if (ownerMethodReference != null) + return hashCode * hashCodeMultiplier + MethodReferenceComparer.GetHashCodeFor (ownerMethodReference); + + throw new InvalidOperationException ("Generic parameter encountered with invalid owner"); + } + + if (metadataType == MetadataType.ByReference) { + var byReferenceType = (ByReferenceType)obj; + return GetHashCodeFor (byReferenceType.ElementType) * hashCodeMultiplier * byReferenceMultiplier; + } + + if (metadataType == MetadataType.Pointer) { + var pointerType = (PointerType)obj; + return GetHashCodeFor (pointerType.ElementType) * hashCodeMultiplier * pointerMultiplier; + } + + if (metadataType == MetadataType.RequiredModifier) { + var requiredModifierType = (RequiredModifierType)obj; + var hashCode = GetHashCodeFor (requiredModifierType.ElementType) * requiredModifierMultiplier; + hashCode = hashCode * hashCodeMultiplier + GetHashCodeFor (requiredModifierType.ModifierType); + return hashCode; + } + + if (metadataType == MetadataType.OptionalModifier) { + var optionalModifierType = (OptionalModifierType)obj; + var hashCode = GetHashCodeFor (optionalModifierType.ElementType) * optionalModifierMultiplier; + hashCode = hashCode * hashCodeMultiplier + GetHashCodeFor (optionalModifierType.ModifierType); + return hashCode; + } + + if (metadataType == MetadataType.Pinned) { + var pinnedType = (PinnedType)obj; + return GetHashCodeFor (pinnedType.ElementType) * hashCodeMultiplier * pinnedMultiplier; + } + + if (metadataType == MetadataType.Sentinel) { + var sentinelType = (SentinelType)obj; + return GetHashCodeFor (sentinelType.ElementType) * hashCodeMultiplier * sentinelMultiplier; + } + + if (metadataType == MetadataType.FunctionPointer) { + throw new NotImplementedException ("We currently don't handle function pointer types."); + } + + return obj.Namespace.GetHashCode () * hashCodeMultiplier + obj.FullName.GetHashCode (); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeReferenceEqualityComparer.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeReferenceEqualityComparer.cs.meta new file mode 100644 index 0000000..e8ce2b4 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeReferenceEqualityComparer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5e048a822386ae34ab1124eaf29e1d84 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeResolver.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeResolver.cs new file mode 100644 index 0000000..69b8507 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeResolver.cs @@ -0,0 +1,220 @@ +using MonoFN.Cecil.Cil; +using System; + +namespace MonoFN.Cecil { + internal sealed class TypeResolver { + private readonly IGenericInstance _typeDefinitionContext; + private readonly IGenericInstance _methodDefinitionContext; + + public static TypeResolver For (TypeReference typeReference) + { + return typeReference.IsGenericInstance ? new TypeResolver ((GenericInstanceType)typeReference) : new TypeResolver (); + } + + public static TypeResolver For (TypeReference typeReference, MethodReference methodReference) + { + return new TypeResolver (typeReference as GenericInstanceType, methodReference as GenericInstanceMethod); + } + + public TypeResolver () + { + + } + + public TypeResolver (GenericInstanceType typeDefinitionContext) + { + _typeDefinitionContext = typeDefinitionContext; + } + + public TypeResolver (GenericInstanceMethod methodDefinitionContext) + { + _methodDefinitionContext = methodDefinitionContext; + } + + public TypeResolver (GenericInstanceType typeDefinitionContext, GenericInstanceMethod methodDefinitionContext) + { + _typeDefinitionContext = typeDefinitionContext; + _methodDefinitionContext = methodDefinitionContext; + } + + public MethodReference Resolve (MethodReference method) + { + var methodReference = method; + if (IsDummy ()) + return methodReference; + + var declaringType = Resolve (method.DeclaringType); + + var genericInstanceMethod = method as GenericInstanceMethod; + if (genericInstanceMethod != null) { + methodReference = new MethodReference (method.Name, method.ReturnType, declaringType); + + foreach (var p in method.Parameters) + methodReference.Parameters.Add (new ParameterDefinition (p.Name, p.Attributes, p.ParameterType)); + + foreach (var gp in genericInstanceMethod.ElementMethod.GenericParameters) + methodReference.GenericParameters.Add (new GenericParameter (gp.Name, methodReference)); + + methodReference.HasThis = method.HasThis; + + var m = new GenericInstanceMethod (methodReference); + foreach (var ga in genericInstanceMethod.GenericArguments) { + m.GenericArguments.Add (Resolve (ga)); + } + + methodReference = m; + } else { + methodReference = new MethodReference (method.Name, method.ReturnType, declaringType); + + foreach (var gp in method.GenericParameters) + methodReference.GenericParameters.Add (new GenericParameter (gp.Name, methodReference)); + + foreach (var p in method.Parameters) + methodReference.Parameters.Add (new ParameterDefinition (p.Name, p.Attributes, p.ParameterType)); + + methodReference.HasThis = method.HasThis; + } + + + return methodReference; + } + + public FieldReference Resolve (FieldReference field) + { + var declaringType = Resolve (field.DeclaringType); + + if (declaringType == field.DeclaringType) + return field; + + return new FieldReference (field.Name, field.FieldType, declaringType); + } + + public TypeReference ResolveReturnType (MethodReference method) + { + return Resolve (GenericParameterResolver.ResolveReturnTypeIfNeeded (method)); + } + + public TypeReference ResolveParameterType (MethodReference method, ParameterReference parameter) + { + return Resolve (GenericParameterResolver.ResolveParameterTypeIfNeeded (method, parameter)); + } + + public TypeReference ResolveVariableType (MethodReference method, VariableReference variable) + { + return Resolve (GenericParameterResolver.ResolveVariableTypeIfNeeded (method, variable)); + } + + public TypeReference ResolveFieldType (FieldReference field) + { + return Resolve (GenericParameterResolver.ResolveFieldTypeIfNeeded (field)); + } + + public TypeReference Resolve (TypeReference typeReference) + { + return Resolve (typeReference, true); + } + + public TypeReference Resolve (TypeReference typeReference, bool includeTypeDefinitions) + { + if (IsDummy ()) + return typeReference; + + if (_typeDefinitionContext != null && _typeDefinitionContext.GenericArguments.Contains (typeReference)) + return typeReference; + if (_methodDefinitionContext != null && _methodDefinitionContext.GenericArguments.Contains (typeReference)) + return typeReference; + + var genericParameter = typeReference as GenericParameter; + if (genericParameter != null) { + if (_typeDefinitionContext != null && _typeDefinitionContext.GenericArguments.Contains (genericParameter)) + return genericParameter; + if (_methodDefinitionContext != null && _methodDefinitionContext.GenericArguments.Contains (genericParameter)) + return genericParameter; + return ResolveGenericParameter (genericParameter); + } + + var arrayType = typeReference as ArrayType; + if (arrayType != null) + return new ArrayType (Resolve (arrayType.ElementType), arrayType.Rank); + + var pointerType = typeReference as PointerType; + if (pointerType != null) + return new PointerType (Resolve (pointerType.ElementType)); + + var byReferenceType = typeReference as ByReferenceType; + if (byReferenceType != null) + return new ByReferenceType (Resolve (byReferenceType.ElementType)); + + var pinnedType = typeReference as PinnedType; + if (pinnedType != null) + return new PinnedType (Resolve (pinnedType.ElementType)); + + var genericInstanceType = typeReference as GenericInstanceType; + if (genericInstanceType != null) { + var newGenericInstanceType = new GenericInstanceType (genericInstanceType.ElementType); + foreach (var genericArgument in genericInstanceType.GenericArguments) + newGenericInstanceType.GenericArguments.Add (Resolve (genericArgument)); + return newGenericInstanceType; + } + + var requiredModType = typeReference as RequiredModifierType; + if (requiredModType != null) + return Resolve (requiredModType.ElementType, includeTypeDefinitions); + + + if (includeTypeDefinitions) { + var typeDefinition = typeReference as TypeDefinition; + if (typeDefinition != null && typeDefinition.HasGenericParameters) { + var newGenericInstanceType = new GenericInstanceType (typeDefinition); + foreach (var gp in typeDefinition.GenericParameters) + newGenericInstanceType.GenericArguments.Add (Resolve (gp)); + return newGenericInstanceType; + } + } + + if (typeReference is TypeSpecification) + throw new NotSupportedException (string.Format ("The type {0} cannot be resolved correctly.", typeReference.FullName)); + + return typeReference; + } + + internal TypeResolver Nested (GenericInstanceMethod genericInstanceMethod) + { + return new TypeResolver (_typeDefinitionContext as GenericInstanceType, genericInstanceMethod); + } + + private TypeReference ResolveGenericParameter (GenericParameter genericParameter) + { + if (genericParameter.Owner == null) + return HandleOwnerlessInvalidILCode (genericParameter); + + var memberReference = genericParameter.Owner as MemberReference; + if (memberReference == null) + throw new NotSupportedException (); + + return genericParameter.Type == GenericParameterType.Type + ? _typeDefinitionContext.GenericArguments [genericParameter.Position] + : (_methodDefinitionContext != null ? _methodDefinitionContext.GenericArguments [genericParameter.Position] : genericParameter); + } + + private TypeReference HandleOwnerlessInvalidILCode (GenericParameter genericParameter) + { + // NOTE: If owner is null and we have a method parameter, then we'll assume that the method parameter + // is actually a type parameter, and we'll use the type parameter from the corresponding position. I think + // this assumption is valid, but if you're visiting this code then I might have been proven wrong. + if (genericParameter.Type == GenericParameterType.Method && (_typeDefinitionContext != null && genericParameter.Position < _typeDefinitionContext.GenericArguments.Count)) + return _typeDefinitionContext.GenericArguments [genericParameter.Position]; + + // NOTE: Owner cannot be null, but sometimes the Mono compiler generates invalid IL and we + // end up in this situation. + // When we do, we assume that the runtime doesn't care about the resolved type of the GenericParameter, + // thus we return a reference to System.Object. + return genericParameter.Module.TypeSystem.Object; + } + + private bool IsDummy () + { + return _typeDefinitionContext == null && _methodDefinitionContext == null; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeResolver.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeResolver.cs.meta new file mode 100644 index 0000000..9b42c4c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeResolver.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 94140ae852ea8954489f238f1e59f865 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeSpecification.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeSpecification.cs new file mode 100644 index 0000000..4be061f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeSpecification.cs @@ -0,0 +1,66 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil { + + public abstract class TypeSpecification : TypeReference { + + readonly TypeReference element_type; + + public TypeReference ElementType { + get { return element_type; } + } + + public override string Name { + get { return element_type.Name; } + set { throw new InvalidOperationException (); } + } + + public override string Namespace { + get { return element_type.Namespace; } + set { throw new InvalidOperationException (); } + } + + public override IMetadataScope Scope { + get { return element_type.Scope; } + set { throw new InvalidOperationException (); } + } + + public override ModuleDefinition Module { + get { return element_type.Module; } + } + + public override string FullName { + get { return element_type.FullName; } + } + + public override bool ContainsGenericParameter { + get { return element_type.ContainsGenericParameter; } + } + + public override MetadataType MetadataType { + get { return (MetadataType)etype; } + } + + internal TypeSpecification (TypeReference type) + : base (null, null) + { + this.element_type = type; + this.token = new MetadataToken (TokenType.TypeSpec); + } + + public override TypeReference GetElementType () + { + return element_type.GetElementType (); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeSpecification.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeSpecification.cs.meta new file mode 100644 index 0000000..2d6b903 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeSpecification.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4c1d979e44d835a4dbcdefa35a45a07a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeSystem.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeSystem.cs new file mode 100644 index 0000000..686156c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeSystem.cs @@ -0,0 +1,330 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.Metadata; +using System; + +namespace MonoFN.Cecil { + + public abstract class TypeSystem { + + sealed class CoreTypeSystem : TypeSystem { + + public CoreTypeSystem (ModuleDefinition module) + : base (module) + { + } + + internal override TypeReference LookupType (string @namespace, string name) + { + var type = LookupTypeDefinition (@namespace, name) ?? LookupTypeForwarded (@namespace, name); + if (type != null) + return type; + + throw new NotSupportedException (); + } + + TypeReference LookupTypeDefinition (string @namespace, string name) + { + var metadata = module.MetadataSystem; + if (metadata.Types == null) + Initialize (module.Types); + + return module.Read (new Row (@namespace, name), (row, reader) => { + var types = reader.metadata.Types; + + for (int i = 0; i < types.Length; i++) { + if (types [i] == null) + types [i] = reader.GetTypeDefinition ((uint)i + 1); + + var type = types [i]; + + if (type.Name == row.Col2 && type.Namespace == row.Col1) + return type; + } + + return null; + }); + } + + TypeReference LookupTypeForwarded (string @namespace, string name) + { + if (!module.HasExportedTypes) + return null; + + var exported_types = module.ExportedTypes; + for (int i = 0; i < exported_types.Count; i++) { + var exported_type = exported_types [i]; + + if (exported_type.Name == name && exported_type.Namespace == @namespace) + return exported_type.CreateReference (); + } + + return null; + } + + static void Initialize (object obj) + { + } + } + + sealed class CommonTypeSystem : TypeSystem { + + AssemblyNameReference core_library; + + public CommonTypeSystem (ModuleDefinition module) + : base (module) + { + } + + internal override TypeReference LookupType (string @namespace, string name) + { + return CreateTypeReference (@namespace, name); + } + + public AssemblyNameReference GetCoreLibraryReference () + { + if (core_library != null) + return core_library; + + if (module.TryGetCoreLibraryReference (out core_library)) + return core_library; + + core_library = new AssemblyNameReference { + Name = Mixin.mscorlib, + Version = GetCorlibVersion (), + PublicKeyToken = new byte [] { 0xb7, 0x7a, 0x5c, 0x56, 0x19, 0x34, 0xe0, 0x89 }, + }; + + module.AssemblyReferences.Add (core_library); + + return core_library; + } + + Version GetCorlibVersion () + { + switch (module.Runtime) { + case TargetRuntime.Net_1_0: + case TargetRuntime.Net_1_1: + return new Version (1, 0, 0, 0); + case TargetRuntime.Net_2_0: + return new Version (2, 0, 0, 0); + case TargetRuntime.Net_4_0: + return new Version (4, 0, 0, 0); + default: + throw new NotSupportedException (); + } + } + + TypeReference CreateTypeReference (string @namespace, string name) + { + return new TypeReference (@namespace, name, module, GetCoreLibraryReference ()); + } + } + + readonly ModuleDefinition module; + + TypeReference type_object; + TypeReference type_void; + TypeReference type_bool; + TypeReference type_char; + TypeReference type_sbyte; + TypeReference type_byte; + TypeReference type_int16; + TypeReference type_uint16; + TypeReference type_int32; + TypeReference type_uint32; + TypeReference type_int64; + TypeReference type_uint64; + TypeReference type_single; + TypeReference type_double; + TypeReference type_intptr; + TypeReference type_uintptr; + TypeReference type_string; + TypeReference type_typedref; + + TypeSystem (ModuleDefinition module) + { + this.module = module; + } + + internal static TypeSystem CreateTypeSystem (ModuleDefinition module) + { + if (module.IsCoreLibrary ()) + return new CoreTypeSystem (module); + + return new CommonTypeSystem (module); + } + + internal abstract TypeReference LookupType (string @namespace, string name); + + TypeReference LookupSystemType (ref TypeReference reference, string name, ElementType element_type) + { + lock (module.SyncRoot) { + if (reference != null) + return reference; + var type = LookupType ("System", name); + type.etype = element_type; + return reference = type; + } + } + + TypeReference LookupSystemValueType (ref TypeReference typeRef, string name, ElementType element_type) + { + lock (module.SyncRoot) { + if (typeRef != null) + return typeRef; + var type = LookupType ("System", name); + type.etype = element_type; + type.KnownValueType (); + return typeRef = type; + } + } + + [Obsolete ("Use CoreLibrary")] + public IMetadataScope Corlib { + get { return CoreLibrary; } + } + + public IMetadataScope CoreLibrary { + get { + var common = this as CommonTypeSystem; + if (common == null) + return module; + + return common.GetCoreLibraryReference (); + } + } + + public TypeReference Object { + get { return type_object ?? (LookupSystemType (ref type_object, "Object", ElementType.Object)); } + } + + public TypeReference Void { + get { return type_void ?? (LookupSystemType (ref type_void, "Void", ElementType.Void)); } + } + + public TypeReference Boolean { + get { return type_bool ?? (LookupSystemValueType (ref type_bool, "Boolean", ElementType.Boolean)); } + } + + public TypeReference Char { + get { return type_char ?? (LookupSystemValueType (ref type_char, "Char", ElementType.Char)); } + } + + public TypeReference SByte { + get { return type_sbyte ?? (LookupSystemValueType (ref type_sbyte, "SByte", ElementType.I1)); } + } + + public TypeReference Byte { + get { return type_byte ?? (LookupSystemValueType (ref type_byte, "Byte", ElementType.U1)); } + } + + public TypeReference Int16 { + get { return type_int16 ?? (LookupSystemValueType (ref type_int16, "Int16", ElementType.I2)); } + } + + public TypeReference UInt16 { + get { return type_uint16 ?? (LookupSystemValueType (ref type_uint16, "UInt16", ElementType.U2)); } + } + + public TypeReference Int32 { + get { return type_int32 ?? (LookupSystemValueType (ref type_int32, "Int32", ElementType.I4)); } + } + + public TypeReference UInt32 { + get { return type_uint32 ?? (LookupSystemValueType (ref type_uint32, "UInt32", ElementType.U4)); } + } + + public TypeReference Int64 { + get { return type_int64 ?? (LookupSystemValueType (ref type_int64, "Int64", ElementType.I8)); } + } + + public TypeReference UInt64 { + get { return type_uint64 ?? (LookupSystemValueType (ref type_uint64, "UInt64", ElementType.U8)); } + } + + public TypeReference Single { + get { return type_single ?? (LookupSystemValueType (ref type_single, "Single", ElementType.R4)); } + } + + public TypeReference Double { + get { return type_double ?? (LookupSystemValueType (ref type_double, "Double", ElementType.R8)); } + } + + public TypeReference IntPtr { + get { return type_intptr ?? (LookupSystemValueType (ref type_intptr, "IntPtr", ElementType.I)); } + } + + public TypeReference UIntPtr { + get { return type_uintptr ?? (LookupSystemValueType (ref type_uintptr, "UIntPtr", ElementType.U)); } + } + + public TypeReference String { + get { return type_string ?? (LookupSystemType (ref type_string, "String", ElementType.String)); } + } + + public TypeReference TypedReference { + get { return type_typedref ?? (LookupSystemValueType (ref type_typedref, "TypedReference", ElementType.TypedByRef)); } + } + } + + static partial class Mixin { + + public const string mscorlib = "mscorlib"; + public const string system_runtime = "System.Runtime"; + public const string system_private_corelib = "System.Private.CoreLib"; + public const string netstandard = "netstandard"; + + public static bool TryGetCoreLibraryReference (this ModuleDefinition module, out AssemblyNameReference reference) + { + var references = module.AssemblyReferences; + + for (int i = 0; i < references.Count; i++) { + reference = references [i]; + if (IsCoreLibrary (reference)) + return true; + } + + reference = null; + return false; + + } + + public static bool IsCoreLibrary (this ModuleDefinition module) + { + if (module.Assembly == null) + return false; + + if (!IsCoreLibrary (module.Assembly.Name)) + return false; + + if (module.HasImage && module.Read (module, (m, reader) => reader.image.GetTableLength (Table.AssemblyRef) > 0)) + return false; + + return true; + } + + public static void KnownValueType (this TypeReference type) + { + if (!type.IsDefinition) + type.IsValueType = true; + } + + static bool IsCoreLibrary (AssemblyNameReference reference) + { + var name = reference.Name; + return name == mscorlib + || name == system_runtime + || name == system_private_corelib + || name == netstandard; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeSystem.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeSystem.cs.meta new file mode 100644 index 0000000..20fec27 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeSystem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 68c81a3fab6a4ee4dbfc5e83e365d1c9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/VariantType.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/VariantType.cs new file mode 100644 index 0000000..9f5ed54 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/VariantType.cs @@ -0,0 +1,37 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +namespace MonoFN.Cecil { + + public enum VariantType { + None = 0, + I2 = 2, + I4 = 3, + R4 = 4, + R8 = 5, + CY = 6, + Date = 7, + BStr = 8, + Dispatch = 9, + Error = 10, + Bool = 11, + Variant = 12, + Unknown = 13, + Decimal = 14, + I1 = 16, + UI1 = 17, + UI2 = 18, + UI4 = 19, + I8 = 20, + UI8 = 21, + Int = 22, + UInt = 23 + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/VariantType.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/VariantType.cs.meta new file mode 100644 index 0000000..5bc5518 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/VariantType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b01b9a572a6975b4c806b3269b858cb1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/WindowsRuntimeProjections.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/WindowsRuntimeProjections.cs new file mode 100644 index 0000000..d505b28 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/WindowsRuntimeProjections.cs @@ -0,0 +1,959 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Threading; + +namespace MonoFN.Cecil { + + sealed class TypeDefinitionProjection { + + public readonly TypeAttributes Attributes; + public readonly string Name; + public readonly TypeDefinitionTreatment Treatment; + public readonly Collection RedirectedMethods; + public readonly Collection> RedirectedInterfaces; + + public TypeDefinitionProjection (TypeDefinition type, TypeDefinitionTreatment treatment, Collection redirectedMethods, Collection> redirectedInterfaces) + { + Attributes = type.Attributes; + Name = type.Name; + Treatment = treatment; + RedirectedMethods = redirectedMethods; + RedirectedInterfaces = redirectedInterfaces; + } + } + + sealed class TypeReferenceProjection { + + public readonly string Name; + public readonly string Namespace; + public readonly IMetadataScope Scope; + public readonly TypeReferenceTreatment Treatment; + + public TypeReferenceProjection (TypeReference type, TypeReferenceTreatment treatment) + { + Name = type.Name; + Namespace = type.Namespace; + Scope = type.Scope; + Treatment = treatment; + } + } + + sealed class MethodDefinitionProjection { + + public readonly MethodAttributes Attributes; + public readonly MethodImplAttributes ImplAttributes; + public readonly string Name; + public readonly MethodDefinitionTreatment Treatment; + + public MethodDefinitionProjection (MethodDefinition method, MethodDefinitionTreatment treatment) + { + Attributes = method.Attributes; + ImplAttributes = method.ImplAttributes; + Name = method.Name; + Treatment = treatment; + } + } + + sealed class FieldDefinitionProjection { + + public readonly FieldAttributes Attributes; + public readonly FieldDefinitionTreatment Treatment; + + public FieldDefinitionProjection (FieldDefinition field, FieldDefinitionTreatment treatment) + { + Attributes = field.Attributes; + Treatment = treatment; + } + } + + sealed class CustomAttributeValueProjection { + + public readonly AttributeTargets Targets; + public readonly CustomAttributeValueTreatment Treatment; + + public CustomAttributeValueProjection (AttributeTargets targets, CustomAttributeValueTreatment treatment) + { + Targets = targets; + Treatment = treatment; + } + } + + sealed class WindowsRuntimeProjections { + + struct ProjectionInfo { + + public readonly string WinRTNamespace; + public readonly string ClrNamespace; + public readonly string ClrName; + public readonly string ClrAssembly; + public readonly bool Attribute; + + public ProjectionInfo (string winrt_namespace, string clr_namespace, string clr_name, string clr_assembly, bool attribute = false) + { + WinRTNamespace = winrt_namespace; + ClrNamespace = clr_namespace; + ClrName = clr_name; + ClrAssembly = clr_assembly; + Attribute = attribute; + } + } + + static readonly Version version = new Version (4, 0, 0, 0); + + static readonly byte [] contract_pk_token = { + 0xB0, 0x3F, 0x5F, 0x7F, 0x11, 0xD5, 0x0A, 0x3A + }; + + static readonly byte [] contract_pk = { + 0x00, 0x24, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, + 0x00, 0x24, 0x00, 0x00, 0x52, 0x53, 0x41, 0x31, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x07, 0xD1, 0xFA, 0x57, 0xC4, 0xAE, 0xD9, 0xF0, 0xA3, 0x2E, 0x84, 0xAA, 0x0F, 0xAE, 0xFD, 0x0D, + 0xE9, 0xE8, 0xFD, 0x6A, 0xEC, 0x8F, 0x87, 0xFB, 0x03, 0x76, 0x6C, 0x83, 0x4C, 0x99, 0x92, 0x1E, + 0xB2, 0x3B, 0xE7, 0x9A, 0xD9, 0xD5, 0xDC, 0xC1, 0xDD, 0x9A, 0xD2, 0x36, 0x13, 0x21, 0x02, 0x90, + 0x0B, 0x72, 0x3C, 0xF9, 0x80, 0x95, 0x7F, 0xC4, 0xE1, 0x77, 0x10, 0x8F, 0xC6, 0x07, 0x77, 0x4F, + 0x29, 0xE8, 0x32, 0x0E, 0x92, 0xEA, 0x05, 0xEC, 0xE4, 0xE8, 0x21, 0xC0, 0xA5, 0xEF, 0xE8, 0xF1, + 0x64, 0x5C, 0x4C, 0x0C, 0x93, 0xC1, 0xAB, 0x99, 0x28, 0x5D, 0x62, 0x2C, 0xAA, 0x65, 0x2C, 0x1D, + 0xFA, 0xD6, 0x3D, 0x74, 0x5D, 0x6F, 0x2D, 0xE5, 0xF1, 0x7E, 0x5E, 0xAF, 0x0F, 0xC4, 0x96, 0x3D, + 0x26, 0x1C, 0x8A, 0x12, 0x43, 0x65, 0x18, 0x20, 0x6D, 0xC0, 0x93, 0x34, 0x4D, 0x5A, 0xD2, 0x93 + }; + + static Dictionary projections; + + static Dictionary Projections { + get { + if (projections != null) + return projections; + + + var new_projections = new Dictionary { + { "AttributeTargets", new ProjectionInfo ("Windows.Foundation.Metadata", "System", "AttributeTargets", "System.Runtime") }, + { "AttributeUsageAttribute", new ProjectionInfo ("Windows.Foundation.Metadata", "System", "AttributeUsageAttribute", "System.Runtime", attribute: true) }, + { "Color", new ProjectionInfo ("Windows.UI", "Windows.UI", "Color", "System.Runtime.WindowsRuntime") }, + { "CornerRadius", new ProjectionInfo ("Windows.UI.Xaml", "Windows.UI.Xaml", "CornerRadius", "System.Runtime.WindowsRuntime.UI.Xaml") }, + { "DateTime", new ProjectionInfo ("Windows.Foundation", "System", "DateTimeOffset", "System.Runtime") }, + { "Duration", new ProjectionInfo ("Windows.UI.Xaml", "Windows.UI.Xaml", "Duration", "System.Runtime.WindowsRuntime.UI.Xaml") }, + { "DurationType", new ProjectionInfo ("Windows.UI.Xaml", "Windows.UI.Xaml", "DurationType", "System.Runtime.WindowsRuntime.UI.Xaml") }, + { "EventHandler`1", new ProjectionInfo ("Windows.Foundation", "System", "EventHandler`1", "System.Runtime") }, + { "EventRegistrationToken", new ProjectionInfo ("Windows.Foundation", "System.Runtime.InteropServices.WindowsRuntime", "EventRegistrationToken", "System.Runtime.InteropServices.WindowsRuntime") }, + { "GeneratorPosition", new ProjectionInfo ("Windows.UI.Xaml.Controls.Primitives", "Windows.UI.Xaml.Controls.Primitives", "GeneratorPosition", "System.Runtime.WindowsRuntime.UI.Xaml") }, + { "GridLength", new ProjectionInfo ("Windows.UI.Xaml", "Windows.UI.Xaml", "GridLength", "System.Runtime.WindowsRuntime.UI.Xaml") }, + { "GridUnitType", new ProjectionInfo ("Windows.UI.Xaml", "Windows.UI.Xaml", "GridUnitType", "System.Runtime.WindowsRuntime.UI.Xaml") }, + { "HResult", new ProjectionInfo ("Windows.Foundation", "System", "Exception", "System.Runtime") }, + { "IBindableIterable", new ProjectionInfo ("Windows.UI.Xaml.Interop", "System.Collections", "IEnumerable", "System.Runtime") }, + { "IBindableVector", new ProjectionInfo ("Windows.UI.Xaml.Interop", "System.Collections", "IList", "System.Runtime") }, + { "IClosable", new ProjectionInfo ("Windows.Foundation", "System", "IDisposable", "System.Runtime") }, + { "ICommand", new ProjectionInfo ("Windows.UI.Xaml.Input", "System.Windows.Input", "ICommand", "System.ObjectModel") }, + { "IIterable`1", new ProjectionInfo ("Windows.Foundation.Collections", "System.Collections.Generic", "IEnumerable`1", "System.Runtime") }, + { "IKeyValuePair`2", new ProjectionInfo ("Windows.Foundation.Collections", "System.Collections.Generic", "KeyValuePair`2", "System.Runtime") }, + { "IMapView`2", new ProjectionInfo ("Windows.Foundation.Collections", "System.Collections.Generic", "IReadOnlyDictionary`2", "System.Runtime") }, + { "IMap`2", new ProjectionInfo ("Windows.Foundation.Collections", "System.Collections.Generic", "IDictionary`2", "System.Runtime") }, + { "INotifyCollectionChanged", new ProjectionInfo ("Windows.UI.Xaml.Interop", "System.Collections.Specialized", "INotifyCollectionChanged", "System.ObjectModel") }, + { "INotifyPropertyChanged", new ProjectionInfo ("Windows.UI.Xaml.Data", "System.ComponentModel", "INotifyPropertyChanged", "System.ObjectModel") }, + { "IReference`1", new ProjectionInfo ("Windows.Foundation", "System", "Nullable`1", "System.Runtime") }, + { "IVectorView`1", new ProjectionInfo ("Windows.Foundation.Collections", "System.Collections.Generic", "IReadOnlyList`1", "System.Runtime") }, + { "IVector`1", new ProjectionInfo ("Windows.Foundation.Collections", "System.Collections.Generic", "IList`1", "System.Runtime") }, + { "KeyTime", new ProjectionInfo ("Windows.UI.Xaml.Media.Animation", "Windows.UI.Xaml.Media.Animation", "KeyTime", "System.Runtime.WindowsRuntime.UI.Xaml") }, + { "Matrix", new ProjectionInfo ("Windows.UI.Xaml.Media", "Windows.UI.Xaml.Media", "Matrix", "System.Runtime.WindowsRuntime.UI.Xaml") }, + { "Matrix3D", new ProjectionInfo ("Windows.UI.Xaml.Media.Media3D", "Windows.UI.Xaml.Media.Media3D", "Matrix3D", "System.Runtime.WindowsRuntime.UI.Xaml") }, + { "Matrix3x2", new ProjectionInfo ("Windows.Foundation.Numerics", "System.Numerics", "Matrix3x2", "System.Numerics.Vectors") }, + { "Matrix4x4", new ProjectionInfo ("Windows.Foundation.Numerics", "System.Numerics", "Matrix4x4", "System.Numerics.Vectors") }, + { "NotifyCollectionChangedAction", new ProjectionInfo ("Windows.UI.Xaml.Interop", "System.Collections.Specialized", "NotifyCollectionChangedAction", "System.ObjectModel") }, + { "NotifyCollectionChangedEventArgs", new ProjectionInfo ("Windows.UI.Xaml.Interop", "System.Collections.Specialized", "NotifyCollectionChangedEventArgs", "System.ObjectModel") }, + { "NotifyCollectionChangedEventHandler", new ProjectionInfo ("Windows.UI.Xaml.Interop", "System.Collections.Specialized", "NotifyCollectionChangedEventHandler", "System.ObjectModel") }, + { "Plane", new ProjectionInfo ("Windows.Foundation.Numerics", "System.Numerics", "Plane", "System.Numerics.Vectors") }, + { "Point", new ProjectionInfo ("Windows.Foundation", "Windows.Foundation", "Point", "System.Runtime.WindowsRuntime") }, + { "PropertyChangedEventArgs", new ProjectionInfo ("Windows.UI.Xaml.Data", "System.ComponentModel", "PropertyChangedEventArgs", "System.ObjectModel") }, + { "PropertyChangedEventHandler", new ProjectionInfo ("Windows.UI.Xaml.Data", "System.ComponentModel", "PropertyChangedEventHandler", "System.ObjectModel") }, + { "Quaternion", new ProjectionInfo ("Windows.Foundation.Numerics", "System.Numerics", "Quaternion", "System.Numerics.Vectors") }, + { "Rect", new ProjectionInfo ("Windows.Foundation", "Windows.Foundation", "Rect", "System.Runtime.WindowsRuntime") }, + { "RepeatBehavior", new ProjectionInfo ("Windows.UI.Xaml.Media.Animation", "Windows.UI.Xaml.Media.Animation", "RepeatBehavior", "System.Runtime.WindowsRuntime.UI.Xaml") }, + { "RepeatBehaviorType", new ProjectionInfo ("Windows.UI.Xaml.Media.Animation", "Windows.UI.Xaml.Media.Animation", "RepeatBehaviorType", "System.Runtime.WindowsRuntime.UI.Xaml") }, + { "Size", new ProjectionInfo ("Windows.Foundation", "Windows.Foundation", "Size", "System.Runtime.WindowsRuntime") }, + { "Thickness", new ProjectionInfo ("Windows.UI.Xaml", "Windows.UI.Xaml", "Thickness", "System.Runtime.WindowsRuntime.UI.Xaml") }, + { "TimeSpan", new ProjectionInfo ("Windows.Foundation", "System", "TimeSpan", "System.Runtime") }, + { "TypeName", new ProjectionInfo ("Windows.UI.Xaml.Interop", "System", "Type", "System.Runtime") }, + { "Uri", new ProjectionInfo ("Windows.Foundation", "System", "Uri", "System.Runtime") }, + { "Vector2", new ProjectionInfo ("Windows.Foundation.Numerics", "System.Numerics", "Vector2", "System.Numerics.Vectors") }, + { "Vector3", new ProjectionInfo ("Windows.Foundation.Numerics", "System.Numerics", "Vector3", "System.Numerics.Vectors") }, + { "Vector4", new ProjectionInfo ("Windows.Foundation.Numerics", "System.Numerics", "Vector4", "System.Numerics.Vectors") }, + }; + + Interlocked.CompareExchange (ref projections, new_projections, null); + return projections; + } + } + + readonly ModuleDefinition module; + Version corlib_version = new Version (255, 255, 255, 255); + AssemblyNameReference [] virtual_references; + + AssemblyNameReference [] VirtualReferences { + get { + if (virtual_references == null) { + // force module to read its assembly references. that will in turn initialize virtual_references + Mixin.Read (module.AssemblyReferences); + } + + return virtual_references; + } + } + + public WindowsRuntimeProjections (ModuleDefinition module) + { + this.module = module; + } + + public static void Project (TypeDefinition type) + { + var treatment = TypeDefinitionTreatment.None; + var metadata_kind = type.Module.MetadataKind; + Collection redirectedMethods = null; + Collection> redirectedInterfaces = null; + + if (type.IsWindowsRuntime) { + if (metadata_kind == MetadataKind.WindowsMetadata) { + treatment = GetWellKnownTypeDefinitionTreatment (type); + if (treatment != TypeDefinitionTreatment.None) { + ApplyProjection (type, new TypeDefinitionProjection (type, treatment, redirectedMethods, redirectedInterfaces)); + return; + } + + var base_type = type.BaseType; + if (base_type != null && IsAttribute (base_type)) { + treatment = TypeDefinitionTreatment.NormalAttribute; + } else { + treatment = GenerateRedirectionInformation (type, out redirectedMethods, out redirectedInterfaces); + } + } else if (metadata_kind == MetadataKind.ManagedWindowsMetadata && NeedsWindowsRuntimePrefix (type)) + treatment = TypeDefinitionTreatment.PrefixWindowsRuntimeName; + + if (treatment == TypeDefinitionTreatment.PrefixWindowsRuntimeName || treatment == TypeDefinitionTreatment.NormalType) + if (!type.IsInterface && HasAttribute (type, "Windows.UI.Xaml", "TreatAsAbstractComposableClassAttribute")) + treatment |= TypeDefinitionTreatment.Abstract; + } else if (metadata_kind == MetadataKind.ManagedWindowsMetadata && IsClrImplementationType (type)) + treatment = TypeDefinitionTreatment.UnmangleWindowsRuntimeName; + + if (treatment != TypeDefinitionTreatment.None) + ApplyProjection (type, new TypeDefinitionProjection (type, treatment, redirectedMethods, redirectedInterfaces)); + } + + static TypeDefinitionTreatment GetWellKnownTypeDefinitionTreatment (TypeDefinition type) + { + ProjectionInfo info; + if (!Projections.TryGetValue (type.Name, out info)) + return TypeDefinitionTreatment.None; + + var treatment = info.Attribute ? TypeDefinitionTreatment.RedirectToClrAttribute : TypeDefinitionTreatment.RedirectToClrType; + + if (type.Namespace == info.ClrNamespace) + return treatment; + + if (type.Namespace == info.WinRTNamespace) + return treatment | TypeDefinitionTreatment.Internal; + + return TypeDefinitionTreatment.None; + } + + private static TypeDefinitionTreatment GenerateRedirectionInformation (TypeDefinition type, out Collection redirectedMethods, out Collection> redirectedInterfaces) + { + bool implementsProjectedInterface = false; + redirectedMethods = null; + redirectedInterfaces = null; + + foreach (var implementedInterface in type.Interfaces) { + if (IsRedirectedType (implementedInterface.InterfaceType)) { + implementsProjectedInterface = true; + break; + } + } + + if (!implementsProjectedInterface) + return TypeDefinitionTreatment.NormalType; + + var allImplementedInterfaces = new HashSet (new TypeReferenceEqualityComparer ()); + redirectedMethods = new Collection (); + redirectedInterfaces = new Collection> (); + + foreach (var @interface in type.Interfaces) { + var interfaceType = @interface.InterfaceType; + + if (IsRedirectedType (interfaceType)) { + allImplementedInterfaces.Add (interfaceType); + CollectImplementedInterfaces (interfaceType, allImplementedInterfaces); + } + } + + foreach (var implementedInterface in type.Interfaces) { + var interfaceType = implementedInterface.InterfaceType; + if (IsRedirectedType (implementedInterface.InterfaceType)) { + var etype = interfaceType.GetElementType (); + var unprojectedType = new TypeReference (etype.Namespace, etype.Name, etype.Module, etype.Scope) { + DeclaringType = etype.DeclaringType, + projection = etype.projection + }; + + RemoveProjection (unprojectedType); + + var genericInstanceType = interfaceType as GenericInstanceType; + if (genericInstanceType != null) { + var genericUnprojectedType = new GenericInstanceType (unprojectedType); + foreach (var genericArgument in genericInstanceType.GenericArguments) + genericUnprojectedType.GenericArguments.Add (genericArgument); + + unprojectedType = genericUnprojectedType; + } + + var unprojectedInterface = new InterfaceImplementation (unprojectedType); + redirectedInterfaces.Add (new KeyValuePair (implementedInterface, unprojectedInterface)); + } + } + + // Interfaces don't inherit methods of the interfaces they implement + if (!type.IsInterface) { + foreach (var implementedInterface in allImplementedInterfaces) { + RedirectInterfaceMethods (implementedInterface, redirectedMethods); + } + } + + return TypeDefinitionTreatment.RedirectImplementedMethods; + } + + private static void CollectImplementedInterfaces (TypeReference type, HashSet results) + { + var typeResolver = TypeResolver.For (type); + var typeDef = type.Resolve (); + + foreach (var implementedInterface in typeDef.Interfaces) { + var interfaceType = typeResolver.Resolve (implementedInterface.InterfaceType); + results.Add (interfaceType); + CollectImplementedInterfaces (interfaceType, results); + } + } + + private static void RedirectInterfaceMethods (TypeReference interfaceType, Collection redirectedMethods) + { + var typeResolver = TypeResolver.For (interfaceType); + var typeDef = interfaceType.Resolve (); + + foreach (var method in typeDef.Methods) { + var redirectedMethod = new MethodDefinition (method.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot, typeResolver.Resolve (method.ReturnType)); + redirectedMethod.ImplAttributes = MethodImplAttributes.Runtime; + + foreach (var parameter in method.Parameters) { + redirectedMethod.Parameters.Add (new ParameterDefinition (parameter.Name, parameter.Attributes, typeResolver.Resolve (parameter.ParameterType))); + } + + redirectedMethod.Overrides.Add (typeResolver.Resolve (method)); + redirectedMethods.Add (redirectedMethod); + } + } + + private static bool IsRedirectedType (TypeReference type) + { + var typeRefProjection = type.GetElementType ().projection as TypeReferenceProjection; + return typeRefProjection != null && typeRefProjection.Treatment == TypeReferenceTreatment.UseProjectionInfo; + } + + static bool NeedsWindowsRuntimePrefix (TypeDefinition type) + { + if ((type.Attributes & (TypeAttributes.VisibilityMask | TypeAttributes.Interface)) != TypeAttributes.Public) + return false; + + var base_type = type.BaseType; + if (base_type == null || base_type.MetadataToken.TokenType != TokenType.TypeRef) + return false; + + if (base_type.Namespace == "System") + switch (base_type.Name) { + case "Attribute": + case "MulticastDelegate": + case "ValueType": + return false; + } + + return true; + } + + public static bool IsClrImplementationType (TypeDefinition type) + { + if ((type.Attributes & (TypeAttributes.VisibilityMask | TypeAttributes.SpecialName)) != TypeAttributes.SpecialName) + return false; + return type.Name.StartsWith (""); + } + + public static void ApplyProjection (TypeDefinition type, TypeDefinitionProjection projection) + { + if (projection == null) + return; + + var treatment = projection.Treatment; + + switch (treatment & TypeDefinitionTreatment.KindMask) { + case TypeDefinitionTreatment.NormalType: + type.Attributes |= TypeAttributes.WindowsRuntime | TypeAttributes.Import; + break; + + case TypeDefinitionTreatment.NormalAttribute: + type.Attributes |= TypeAttributes.WindowsRuntime | TypeAttributes.Sealed; + break; + + case TypeDefinitionTreatment.UnmangleWindowsRuntimeName: + type.Attributes = type.Attributes & ~TypeAttributes.SpecialName | TypeAttributes.Public; + type.Name = type.Name.Substring ("".Length); + break; + + case TypeDefinitionTreatment.PrefixWindowsRuntimeName: + type.Attributes = type.Attributes & ~TypeAttributes.Public | TypeAttributes.Import; + type.Name = "" + type.Name; + break; + + case TypeDefinitionTreatment.RedirectToClrType: + type.Attributes = type.Attributes & ~TypeAttributes.Public | TypeAttributes.Import; + break; + + case TypeDefinitionTreatment.RedirectToClrAttribute: + type.Attributes = type.Attributes & ~TypeAttributes.Public; + break; + + case TypeDefinitionTreatment.RedirectImplementedMethods: { + type.Attributes |= TypeAttributes.WindowsRuntime | TypeAttributes.Import; + + foreach (var redirectedInterfacePair in projection.RedirectedInterfaces) { + type.Interfaces.Add (redirectedInterfacePair.Value); + + foreach (var customAttribute in redirectedInterfacePair.Key.CustomAttributes) + redirectedInterfacePair.Value.CustomAttributes.Add (customAttribute); + + redirectedInterfacePair.Key.CustomAttributes.Clear (); + + foreach (var method in type.Methods) { + foreach (var @override in method.Overrides) { + if (TypeReferenceEqualityComparer.AreEqual (@override.DeclaringType, redirectedInterfacePair.Key.InterfaceType)) { + @override.DeclaringType = redirectedInterfacePair.Value.InterfaceType; + } + } + } + } + + foreach (var method in projection.RedirectedMethods) { + type.Methods.Add (method); + } + } + break; + } + + if ((treatment & TypeDefinitionTreatment.Abstract) != 0) + type.Attributes |= TypeAttributes.Abstract; + + if ((treatment & TypeDefinitionTreatment.Internal) != 0) + type.Attributes &= ~TypeAttributes.Public; + + type.WindowsRuntimeProjection = projection; + } + + public static TypeDefinitionProjection RemoveProjection (TypeDefinition type) + { + if (!type.IsWindowsRuntimeProjection) + return null; + + var projection = type.WindowsRuntimeProjection; + type.WindowsRuntimeProjection = null; + + type.Attributes = projection.Attributes; + type.Name = projection.Name; + + if (projection.Treatment == TypeDefinitionTreatment.RedirectImplementedMethods) { + foreach (var method in projection.RedirectedMethods) { + type.Methods.Remove (method); + } + + foreach (var redirectedInterfacePair in projection.RedirectedInterfaces) { + foreach (var method in type.Methods) { + foreach (var @override in method.Overrides) { + if (TypeReferenceEqualityComparer.AreEqual (@override.DeclaringType, redirectedInterfacePair.Value.InterfaceType)) { + @override.DeclaringType = redirectedInterfacePair.Key.InterfaceType; + } + } + } + + foreach (var customAttribute in redirectedInterfacePair.Value.CustomAttributes) + redirectedInterfacePair.Key.CustomAttributes.Add (customAttribute); + + redirectedInterfacePair.Value.CustomAttributes.Clear (); + type.Interfaces.Remove (redirectedInterfacePair.Value); + } + } + + return projection; + } + + public static void Project (TypeReference type) + { + TypeReferenceTreatment treatment; + + ProjectionInfo info; + if (Projections.TryGetValue (type.Name, out info) && info.WinRTNamespace == type.Namespace) + treatment = TypeReferenceTreatment.UseProjectionInfo; + else + treatment = GetSpecialTypeReferenceTreatment (type); + + if (treatment != TypeReferenceTreatment.None) + ApplyProjection (type, new TypeReferenceProjection (type, treatment)); + } + + static TypeReferenceTreatment GetSpecialTypeReferenceTreatment (TypeReference type) + { + if (type.Namespace == "System") { + if (type.Name == "MulticastDelegate") + return TypeReferenceTreatment.SystemDelegate; + if (type.Name == "Attribute") + return TypeReferenceTreatment.SystemAttribute; + } + + return TypeReferenceTreatment.None; + } + + static bool IsAttribute (TypeReference type) + { + if (type.MetadataToken.TokenType != TokenType.TypeRef) + return false; + return type.Name == "Attribute" && type.Namespace == "System"; + } + + static bool IsEnum (TypeReference type) + { + if (type.MetadataToken.TokenType != TokenType.TypeRef) + return false; + return type.Name == "Enum" && type.Namespace == "System"; + } + + public static void ApplyProjection (TypeReference type, TypeReferenceProjection projection) + { + if (projection == null) + return; + + switch (projection.Treatment) { + case TypeReferenceTreatment.SystemDelegate: + case TypeReferenceTreatment.SystemAttribute: + type.Scope = type.Module.Projections.GetAssemblyReference ("System.Runtime"); + break; + + case TypeReferenceTreatment.UseProjectionInfo: + var info = Projections [type.Name]; + type.Name = info.ClrName; + type.Namespace = info.ClrNamespace; + type.Scope = type.Module.Projections.GetAssemblyReference (info.ClrAssembly); + break; + } + + type.WindowsRuntimeProjection = projection; + } + + public static TypeReferenceProjection RemoveProjection (TypeReference type) + { + if (!type.IsWindowsRuntimeProjection) + return null; + + var projection = type.WindowsRuntimeProjection; + type.WindowsRuntimeProjection = null; + + type.Name = projection.Name; + type.Namespace = projection.Namespace; + type.Scope = projection.Scope; + + return projection; + } + + public static void Project (MethodDefinition method) + { + var treatment = MethodDefinitionTreatment.None; + var other = false; + var declaring_type = method.DeclaringType; + + if (declaring_type.IsWindowsRuntime) { + if (IsClrImplementationType (declaring_type)) + treatment = MethodDefinitionTreatment.None; + else if (declaring_type.IsNested) + treatment = MethodDefinitionTreatment.None; + else if (declaring_type.IsInterface) + treatment = MethodDefinitionTreatment.Runtime | MethodDefinitionTreatment.InternalCall; + else if (declaring_type.Module.MetadataKind == MetadataKind.ManagedWindowsMetadata && !method.IsPublic) + treatment = MethodDefinitionTreatment.None; + else { + other = true; + + var base_type = declaring_type.BaseType; + if (base_type != null && base_type.MetadataToken.TokenType == TokenType.TypeRef) { + switch (GetSpecialTypeReferenceTreatment (base_type)) { + case TypeReferenceTreatment.SystemDelegate: + treatment = MethodDefinitionTreatment.Runtime | MethodDefinitionTreatment.Public; + other = false; + break; + + case TypeReferenceTreatment.SystemAttribute: + treatment = MethodDefinitionTreatment.Runtime | MethodDefinitionTreatment.InternalCall; + other = false; + break; + } + } + } + } + + if (other) { + var seen_redirected = false; + var seen_non_redirected = false; + + foreach (var @override in method.Overrides) { + if (@override.MetadataToken.TokenType == TokenType.MemberRef && ImplementsRedirectedInterface (@override)) { + seen_redirected = true; + } else { + seen_non_redirected = true; + } + } + + if (seen_redirected && !seen_non_redirected) { + treatment = MethodDefinitionTreatment.Runtime | MethodDefinitionTreatment.InternalCall | MethodDefinitionTreatment.Private; + other = false; + } + } + + if (other) + treatment |= GetMethodDefinitionTreatmentFromCustomAttributes (method); + + if (treatment != MethodDefinitionTreatment.None) + ApplyProjection (method, new MethodDefinitionProjection (method, treatment)); + } + + static MethodDefinitionTreatment GetMethodDefinitionTreatmentFromCustomAttributes (MethodDefinition method) + { + var treatment = MethodDefinitionTreatment.None; + + foreach (var attribute in method.CustomAttributes) { + var type = attribute.AttributeType; + if (type.Namespace != "Windows.UI.Xaml") + continue; + if (type.Name == "TreatAsPublicMethodAttribute") + treatment |= MethodDefinitionTreatment.Public; + else if (type.Name == "TreatAsAbstractMethodAttribute") + treatment |= MethodDefinitionTreatment.Abstract; + } + + return treatment; + } + + public static void ApplyProjection (MethodDefinition method, MethodDefinitionProjection projection) + { + if (projection == null) + return; + + var treatment = projection.Treatment; + + if ((treatment & MethodDefinitionTreatment.Abstract) != 0) + method.Attributes |= MethodAttributes.Abstract; + + if ((treatment & MethodDefinitionTreatment.Private) != 0) + method.Attributes = (method.Attributes & ~MethodAttributes.MemberAccessMask) | MethodAttributes.Private; + + if ((treatment & MethodDefinitionTreatment.Public) != 0) + method.Attributes = (method.Attributes & ~MethodAttributes.MemberAccessMask) | MethodAttributes.Public; + + if ((treatment & MethodDefinitionTreatment.Runtime) != 0) + method.ImplAttributes |= MethodImplAttributes.Runtime; + + if ((treatment & MethodDefinitionTreatment.InternalCall) != 0) + method.ImplAttributes |= MethodImplAttributes.InternalCall; + + method.WindowsRuntimeProjection = projection; + } + + public static MethodDefinitionProjection RemoveProjection (MethodDefinition method) + { + if (!method.IsWindowsRuntimeProjection) + return null; + + var projection = method.WindowsRuntimeProjection; + method.WindowsRuntimeProjection = null; + + method.Attributes = projection.Attributes; + method.ImplAttributes = projection.ImplAttributes; + method.Name = projection.Name; + + return projection; + } + + public static void Project (FieldDefinition field) + { + var treatment = FieldDefinitionTreatment.None; + var declaring_type = field.DeclaringType; + + if (declaring_type.Module.MetadataKind == MetadataKind.WindowsMetadata && field.IsRuntimeSpecialName && field.Name == "value__") { + var base_type = declaring_type.BaseType; + if (base_type != null && IsEnum (base_type)) + treatment = FieldDefinitionTreatment.Public; + } + + if (treatment != FieldDefinitionTreatment.None) + ApplyProjection (field, new FieldDefinitionProjection (field, treatment)); + } + + public static void ApplyProjection (FieldDefinition field, FieldDefinitionProjection projection) + { + if (projection == null) + return; + + if (projection.Treatment == FieldDefinitionTreatment.Public) + field.Attributes = (field.Attributes & ~FieldAttributes.FieldAccessMask) | FieldAttributes.Public; + + field.WindowsRuntimeProjection = projection; + } + + public static FieldDefinitionProjection RemoveProjection (FieldDefinition field) + { + if (!field.IsWindowsRuntimeProjection) + return null; + + var projection = field.WindowsRuntimeProjection; + field.WindowsRuntimeProjection = null; + + field.Attributes = projection.Attributes; + + return projection; + } + + static bool ImplementsRedirectedInterface (MemberReference member) + { + var declaring_type = member.DeclaringType; + TypeReference type; + switch (declaring_type.MetadataToken.TokenType) { + case TokenType.TypeRef: + type = declaring_type; + break; + + case TokenType.TypeSpec: + if (!declaring_type.IsGenericInstance) + return false; + + type = ((TypeSpecification)declaring_type).ElementType; + if (type.MetadataType != MetadataType.Class || type.MetadataToken.TokenType != TokenType.TypeRef) + return false; + + break; + + default: + return false; + } + + var projection = RemoveProjection (type); + + var found = false; + + ProjectionInfo info; + if (Projections.TryGetValue (type.Name, out info) && type.Namespace == info.WinRTNamespace) { + found = true; + } + + ApplyProjection (type, projection); + + return found; + } + + + public void AddVirtualReferences (Collection references) + { + var corlib = GetCoreLibrary (references); + corlib_version = corlib.Version; + corlib.Version = version; + + if (virtual_references == null) { + var winrt_references = GetAssemblyReferences (corlib); + Interlocked.CompareExchange (ref virtual_references, winrt_references, null); + } + + foreach (var reference in virtual_references) + references.Add (reference); + } + + public void RemoveVirtualReferences (Collection references) + { + var corlib = GetCoreLibrary (references); + corlib.Version = corlib_version; + + foreach (var reference in VirtualReferences) + references.Remove (reference); + } + + static AssemblyNameReference [] GetAssemblyReferences (AssemblyNameReference corlib) + { + var system_runtime = new AssemblyNameReference ("System.Runtime", version); + var system_runtime_interopservices_windowsruntime = new AssemblyNameReference ("System.Runtime.InteropServices.WindowsRuntime", version); + var system_objectmodel = new AssemblyNameReference ("System.ObjectModel", version); + var system_runtime_windowsruntime = new AssemblyNameReference ("System.Runtime.WindowsRuntime", version); + var system_runtime_windowsruntime_ui_xaml = new AssemblyNameReference ("System.Runtime.WindowsRuntime.UI.Xaml", version); + var system_numerics_vectors = new AssemblyNameReference ("System.Numerics.Vectors", version); + + if (corlib.HasPublicKey) { + system_runtime_windowsruntime.PublicKey = + system_runtime_windowsruntime_ui_xaml.PublicKey = corlib.PublicKey; + + system_runtime.PublicKey = + system_runtime_interopservices_windowsruntime.PublicKey = + system_objectmodel.PublicKey = + system_numerics_vectors.PublicKey = contract_pk; + } else { + system_runtime_windowsruntime.PublicKeyToken = + system_runtime_windowsruntime_ui_xaml.PublicKeyToken = corlib.PublicKeyToken; + + system_runtime.PublicKeyToken = + system_runtime_interopservices_windowsruntime.PublicKeyToken = + system_objectmodel.PublicKeyToken = + system_numerics_vectors.PublicKeyToken = contract_pk_token; + } + + return new [] { + system_runtime, + system_runtime_interopservices_windowsruntime, + system_objectmodel, + system_runtime_windowsruntime, + system_runtime_windowsruntime_ui_xaml, + system_numerics_vectors, + }; + } + + static AssemblyNameReference GetCoreLibrary (Collection references) + { + foreach (var reference in references) + if (reference.Name == "mscorlib") + return reference; + + throw new BadImageFormatException ("Missing mscorlib reference in AssemblyRef table."); + } + + AssemblyNameReference GetAssemblyReference (string name) + { + foreach (var assembly in VirtualReferences) + if (assembly.Name == name) + return assembly; + + throw new Exception (); + } + + public static void Project (ICustomAttributeProvider owner, CustomAttribute attribute) + { + if (!IsWindowsAttributeUsageAttribute (owner, attribute)) + return; + + var treatment = CustomAttributeValueTreatment.None; + var type = (TypeDefinition)owner; + + if (type.Namespace == "Windows.Foundation.Metadata") { + if (type.Name == "VersionAttribute") + treatment = CustomAttributeValueTreatment.VersionAttribute; + else if (type.Name == "DeprecatedAttribute") + treatment = CustomAttributeValueTreatment.DeprecatedAttribute; + } + + if (treatment == CustomAttributeValueTreatment.None) { + var multiple = HasAttribute (type, "Windows.Foundation.Metadata", "AllowMultipleAttribute"); + treatment = multiple ? CustomAttributeValueTreatment.AllowMultiple : CustomAttributeValueTreatment.AllowSingle; + } + + if (treatment != CustomAttributeValueTreatment.None) { + var attribute_targets = (AttributeTargets)attribute.ConstructorArguments [0].Value; + ApplyProjection (attribute, new CustomAttributeValueProjection (attribute_targets, treatment)); + } + } + + static bool IsWindowsAttributeUsageAttribute (ICustomAttributeProvider owner, CustomAttribute attribute) + { + if (owner.MetadataToken.TokenType != TokenType.TypeDef) + return false; + + var constructor = attribute.Constructor; + + if (constructor.MetadataToken.TokenType != TokenType.MemberRef) + return false; + + var declaring_type = constructor.DeclaringType; + + if (declaring_type.MetadataToken.TokenType != TokenType.TypeRef) + return false; + + // declaring type is already projected + return declaring_type.Name == "AttributeUsageAttribute" && declaring_type.Namespace == /*"Windows.Foundation.Metadata"*/"System"; + } + + static bool HasAttribute (TypeDefinition type, string @namespace, string name) + { + foreach (var attribute in type.CustomAttributes) { + var attribute_type = attribute.AttributeType; + if (attribute_type.Name == name && attribute_type.Namespace == @namespace) + return true; + } + return false; + } + + public static void ApplyProjection (CustomAttribute attribute, CustomAttributeValueProjection projection) + { + if (projection == null) + return; + + bool version_or_deprecated; + bool multiple; + + switch (projection.Treatment) { + case CustomAttributeValueTreatment.AllowSingle: + version_or_deprecated = false; + multiple = false; + break; + + case CustomAttributeValueTreatment.AllowMultiple: + version_or_deprecated = false; + multiple = true; + break; + + case CustomAttributeValueTreatment.VersionAttribute: + case CustomAttributeValueTreatment.DeprecatedAttribute: + version_or_deprecated = true; + multiple = true; + break; + + default: + throw new ArgumentException (); + } + + var attribute_targets = (AttributeTargets)attribute.ConstructorArguments [0].Value; + if (version_or_deprecated) + attribute_targets |= AttributeTargets.Constructor | AttributeTargets.Property; + attribute.ConstructorArguments [0] = new CustomAttributeArgument (attribute.ConstructorArguments [0].Type, attribute_targets); + + attribute.Properties.Add (new CustomAttributeNamedArgument ("AllowMultiple", new CustomAttributeArgument (attribute.Module.TypeSystem.Boolean, multiple))); + + attribute.projection = projection; + } + + public static CustomAttributeValueProjection RemoveProjection (CustomAttribute attribute) + { + if (attribute.projection == null) + return null; + + var projection = attribute.projection; + attribute.projection = null; + + attribute.ConstructorArguments [0] = new CustomAttributeArgument (attribute.ConstructorArguments [0].Type, projection.Targets); + attribute.Properties.Clear (); + + return projection; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/WindowsRuntimeProjections.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/WindowsRuntimeProjections.cs.meta new file mode 100644 index 0000000..c747763 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/WindowsRuntimeProjections.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4816f370ee0b9f64ca793643ba5481ae +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic.meta new file mode 100644 index 0000000..7ec9f6e --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dc4bc1e600836e94f9bbc5bd8c7cc47a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic/Collection.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic/Collection.cs new file mode 100644 index 0000000..0c5caf2 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic/Collection.cs @@ -0,0 +1,427 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil; +using System; +using System.Collections; +using System.Collections.Generic; + +namespace MonoFN.Collections.Generic { + + public class Collection : IList, IList { + + internal T [] items; + internal int size; + int version; + + public int Count { + get { return size; } + } + + public T this [int index] { + get { + if (index >= size) + throw new ArgumentOutOfRangeException (); + + return items [index]; + } + set { + CheckIndex (index); + if (index == size) + throw new ArgumentOutOfRangeException (); + + OnSet (value, index); + + items [index] = value; + } + } + + public int Capacity { + get { return items.Length; } + set { + if (value < 0 || value < size) + throw new ArgumentOutOfRangeException (); + + Resize (value); + } + } + + bool ICollection.IsReadOnly { + get { return false; } + } + + bool IList.IsFixedSize { + get { return false; } + } + + bool IList.IsReadOnly { + get { return false; } + } + + object IList.this [int index] { + get { return this [index]; } + set { + CheckIndex (index); + + try { + this [index] = (T)value; + return; + } + catch (InvalidCastException) { + } + catch (NullReferenceException) { + } + + throw new ArgumentException (); + } + } + + int ICollection.Count { + get { return Count; } + } + + bool ICollection.IsSynchronized { + get { return false; } + } + + object ICollection.SyncRoot { + get { return this; } + } + + public Collection () + { + items = Empty.Array; + } + + public Collection (int capacity) + { + if (capacity < 0) + throw new ArgumentOutOfRangeException (); + + items = capacity == 0 + ? Empty.Array + : new T [capacity]; + } + + public Collection (ICollection items) + { + if (items == null) + throw new ArgumentNullException ("items"); + + this.items = new T [items.Count]; + items.CopyTo (this.items, 0); + this.size = this.items.Length; + } + + public void Add (T item) + { + if (size == items.Length) + Grow (1); + + OnAdd (item, size); + + items [size++] = item; + version++; + } + + public bool Contains (T item) + { + return IndexOf (item) != -1; + } + + public int IndexOf (T item) + { + return Array.IndexOf (items, item, 0, size); + } + + public void Insert (int index, T item) + { + CheckIndex (index); + if (size == items.Length) + Grow (1); + + OnInsert (item, index); + + Shift (index, 1); + items [index] = item; + version++; + } + + public void RemoveAt (int index) + { + if (index < 0 || index >= size) + throw new ArgumentOutOfRangeException (); + + var item = items [index]; + + OnRemove (item, index); + + Shift (index, -1); + version++; + } + + public bool Remove (T item) + { + var index = IndexOf (item); + if (index == -1) + return false; + + OnRemove (item, index); + + Shift (index, -1); + version++; + + return true; + } + + public void Clear () + { + OnClear (); + + Array.Clear (items, 0, size); + size = 0; + version++; + } + + public void CopyTo (T [] array, int arrayIndex) + { + Array.Copy (items, 0, array, arrayIndex, size); + } + + public T [] ToArray () + { + var array = new T [size]; + Array.Copy (items, 0, array, 0, size); + return array; + } + + void CheckIndex (int index) + { + if (index < 0 || index > size) + throw new ArgumentOutOfRangeException (); + } + + void Shift (int start, int delta) + { + if (delta < 0) + start -= delta; + + if (start < size) + Array.Copy (items, start, items, start + delta, size - start); + + size += delta; + + if (delta < 0) + Array.Clear (items, size, -delta); + } + + protected virtual void OnAdd (T item, int index) + { + } + + protected virtual void OnInsert (T item, int index) + { + } + + protected virtual void OnSet (T item, int index) + { + } + + protected virtual void OnRemove (T item, int index) + { + } + + protected virtual void OnClear () + { + } + + internal virtual void Grow (int desired) + { + int new_size = size + desired; + if (new_size <= items.Length) + return; + + const int default_capacity = 4; + + new_size = System.Math.Max ( + System.Math.Max (items.Length * 2, default_capacity), + new_size); + + Resize (new_size); + } + + protected void Resize (int new_size) + { + if (new_size == size) + return; + if (new_size < size) + throw new ArgumentOutOfRangeException (); + + items = items.Resize (new_size); + } + + int IList.Add (object value) + { + try { + Add ((T)value); + return size - 1; + } + catch (InvalidCastException) { + } + catch (NullReferenceException) { + } + + throw new ArgumentException (); + } + + void IList.Clear () + { + Clear (); + } + + bool IList.Contains (object value) + { + return ((IList)this).IndexOf (value) > -1; + } + + int IList.IndexOf (object value) + { + try { + return IndexOf ((T)value); + } + catch (InvalidCastException) { + } + catch (NullReferenceException) { + } + + return -1; + } + + void IList.Insert (int index, object value) + { + CheckIndex (index); + + try { + Insert (index, (T)value); + return; + } + catch (InvalidCastException) { + } + catch (NullReferenceException) { + } + + throw new ArgumentException (); + } + + void IList.Remove (object value) + { + try { + Remove ((T)value); + } + catch (InvalidCastException) { + } + catch (NullReferenceException) { + } + } + + void IList.RemoveAt (int index) + { + RemoveAt (index); + } + + void ICollection.CopyTo (Array array, int index) + { + Array.Copy (items, 0, array, index, size); + } + + public Enumerator GetEnumerator () + { + return new Enumerator (this); + } + + IEnumerator IEnumerable.GetEnumerator () + { + return new Enumerator (this); + } + + IEnumerator IEnumerable.GetEnumerator () + { + return new Enumerator (this); + } + + public struct Enumerator : IEnumerator, IDisposable { + + Collection collection; + T current; + + int next; + readonly int version; + + public T Current { + get { return current; } + } + + object IEnumerator.Current { + get { + CheckState (); + + if (next <= 0) + throw new InvalidOperationException (); + + return current; + } + } + + internal Enumerator (Collection collection) + : this () + { + this.collection = collection; + this.version = collection.version; + } + + public bool MoveNext () + { + CheckState (); + + if (next < 0) + return false; + + if (next < collection.size) { + current = collection.items [next++]; + return true; + } + + next = -1; + return false; + } + + public void Reset () + { + CheckState (); + + next = 0; + } + + void CheckState () + { + if (collection == null) + throw new ObjectDisposedException (GetType ().FullName); + + if (version != collection.version) + throw new InvalidOperationException (); + } + + public void Dispose () + { + collection = null; + } + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic/Collection.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic/Collection.cs.meta new file mode 100644 index 0000000..01bd872 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic/Collection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d1f0d8a176addda42a579d710c12a7ec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic/ReadOnlyCollection.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic/ReadOnlyCollection.cs new file mode 100644 index 0000000..736f1c0 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic/ReadOnlyCollection.cs @@ -0,0 +1,101 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; + +namespace MonoFN.Collections.Generic { + + public sealed class ReadOnlyCollection : Collection, ICollection, IList { + + static ReadOnlyCollection empty; + + public static ReadOnlyCollection Empty { + get { + if (empty != null) + return empty; + + Interlocked.CompareExchange (ref empty, new ReadOnlyCollection (), null); + return empty; + } + } + + bool ICollection.IsReadOnly { + get { return true; } + } + + bool IList.IsFixedSize { + get { return true; } + } + + bool IList.IsReadOnly { + get { return true; } + } + + ReadOnlyCollection () + { + } + + public ReadOnlyCollection (T [] array) + { + if (array == null) + throw new ArgumentNullException (); + + Initialize (array, array.Length); + } + + public ReadOnlyCollection (Collection collection) + { + if (collection == null) + throw new ArgumentNullException (); + + Initialize (collection.items, collection.size); + } + + void Initialize (T [] items, int size) + { + this.items = new T [size]; + Array.Copy (items, 0, this.items, 0, size); + this.size = size; + } + + internal override void Grow (int desired) + { + throw new InvalidOperationException (); + } + + protected override void OnAdd (T item, int index) + { + throw new InvalidOperationException (); + } + + protected override void OnClear () + { + throw new InvalidOperationException (); + } + + protected override void OnInsert (T item, int index) + { + throw new InvalidOperationException (); + } + + protected override void OnRemove (T item, int index) + { + throw new InvalidOperationException (); + } + + protected override void OnSet (T item, int index) + { + throw new InvalidOperationException (); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic/ReadOnlyCollection.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic/ReadOnlyCollection.cs.meta new file mode 100644 index 0000000..a89c654 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic/ReadOnlyCollection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d94cb0938831f1349b60e63292a04b67 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography.meta new file mode 100644 index 0000000..65c6957 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 83669b0a87b783648b0d69a7b37e00ab +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography/CryptoConvert.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography/CryptoConvert.cs new file mode 100644 index 0000000..c6eacee --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography/CryptoConvert.cs @@ -0,0 +1,290 @@ +// +// CryptoConvert.cs - Crypto Convertion Routines +// +// Author: +// Sebastien Pouliot +// +// (C) 2003 Motus Technologies Inc. (http://www.motus.com) +// Copyright (C) 2004-2006 Novell Inc. (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Security.Cryptography; + +namespace MonoFN.Security.Cryptography { + + static class CryptoConvert { + + static private int ToInt32LE (byte [] bytes, int offset) + { + return (bytes [offset + 3] << 24) | (bytes [offset + 2] << 16) | (bytes [offset + 1] << 8) | bytes [offset]; + } + + static private uint ToUInt32LE (byte [] bytes, int offset) + { + return (uint)((bytes [offset + 3] << 24) | (bytes [offset + 2] << 16) | (bytes [offset + 1] << 8) | bytes [offset]); + } + + static private byte [] GetBytesLE (int val) + { + return new byte [] { + (byte) (val & 0xff), + (byte) ((val >> 8) & 0xff), + (byte) ((val >> 16) & 0xff), + (byte) ((val >> 24) & 0xff) + }; + } + + static private byte [] Trim (byte [] array) + { + for (int i = 0; i < array.Length; i++) { + if (array [i] != 0x00) { + byte [] result = new byte [array.Length - i]; + Buffer.BlockCopy (array, i, result, 0, result.Length); + return result; + } + } + return null; + } + + static RSA FromCapiPrivateKeyBlob (byte [] blob, int offset) + { + RSAParameters rsap = new RSAParameters (); + try { + if ((blob [offset] != 0x07) || // PRIVATEKEYBLOB (0x07) + (blob [offset + 1] != 0x02) || // Version (0x02) + (blob [offset + 2] != 0x00) || // Reserved (word) + (blob [offset + 3] != 0x00) || + (ToUInt32LE (blob, offset + 8) != 0x32415352)) // DWORD magic = RSA2 + throw new CryptographicException ("Invalid blob header"); + + // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...) + // int algId = ToInt32LE (blob, offset+4); + + // DWORD bitlen + int bitLen = ToInt32LE (blob, offset + 12); + + // DWORD public exponent + byte [] exp = new byte [4]; + Buffer.BlockCopy (blob, offset + 16, exp, 0, 4); + Array.Reverse (exp); + rsap.Exponent = Trim (exp); + + int pos = offset + 20; + // BYTE modulus[rsapubkey.bitlen/8]; + int byteLen = (bitLen >> 3); + rsap.Modulus = new byte [byteLen]; + Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen); + Array.Reverse (rsap.Modulus); + pos += byteLen; + + // BYTE prime1[rsapubkey.bitlen/16]; + int byteHalfLen = (byteLen >> 1); + rsap.P = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.P, 0, byteHalfLen); + Array.Reverse (rsap.P); + pos += byteHalfLen; + + // BYTE prime2[rsapubkey.bitlen/16]; + rsap.Q = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.Q, 0, byteHalfLen); + Array.Reverse (rsap.Q); + pos += byteHalfLen; + + // BYTE exponent1[rsapubkey.bitlen/16]; + rsap.DP = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.DP, 0, byteHalfLen); + Array.Reverse (rsap.DP); + pos += byteHalfLen; + + // BYTE exponent2[rsapubkey.bitlen/16]; + rsap.DQ = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.DQ, 0, byteHalfLen); + Array.Reverse (rsap.DQ); + pos += byteHalfLen; + + // BYTE coefficient[rsapubkey.bitlen/16]; + rsap.InverseQ = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.InverseQ, 0, byteHalfLen); + Array.Reverse (rsap.InverseQ); + pos += byteHalfLen; + + // ok, this is hackish but CryptoAPI support it so... + // note: only works because CRT is used by default + // http://bugzilla.ximian.com/show_bug.cgi?id=57941 + rsap.D = new byte [byteLen]; // must be allocated + if (pos + byteLen + offset <= blob.Length) { + // BYTE privateExponent[rsapubkey.bitlen/8]; + Buffer.BlockCopy (blob, pos, rsap.D, 0, byteLen); + Array.Reverse (rsap.D); + } + } + catch (Exception e) { + throw new CryptographicException ("Invalid blob.", e); + } + + RSA rsa = null; + try { + rsa = RSA.Create (); + rsa.ImportParameters (rsap); + } + catch (CryptographicException) { + // this may cause problem when this code is run under + // the SYSTEM identity on Windows (e.g. ASP.NET). See + // http://bugzilla.ximian.com/show_bug.cgi?id=77559 + bool throws = false; + try { + CspParameters csp = new CspParameters (); + csp.Flags = CspProviderFlags.UseMachineKeyStore; + rsa = new RSACryptoServiceProvider (csp); + rsa.ImportParameters (rsap); + } + catch { + throws = true; + } + + if (throws) { + // rethrow original, not the latter, exception if this fails + throw; + } + } + return rsa; + } + + static RSA FromCapiPublicKeyBlob (byte [] blob, int offset) + { + try { + if ((blob [offset] != 0x06) || // PUBLICKEYBLOB (0x06) + (blob [offset + 1] != 0x02) || // Version (0x02) + (blob [offset + 2] != 0x00) || // Reserved (word) + (blob [offset + 3] != 0x00) || + (ToUInt32LE (blob, offset + 8) != 0x31415352)) // DWORD magic = RSA1 + throw new CryptographicException ("Invalid blob header"); + + // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...) + // int algId = ToInt32LE (blob, offset+4); + + // DWORD bitlen + int bitLen = ToInt32LE (blob, offset + 12); + + // DWORD public exponent + RSAParameters rsap = new RSAParameters (); + rsap.Exponent = new byte [3]; + rsap.Exponent [0] = blob [offset + 18]; + rsap.Exponent [1] = blob [offset + 17]; + rsap.Exponent [2] = blob [offset + 16]; + + int pos = offset + 20; + // BYTE modulus[rsapubkey.bitlen/8]; + int byteLen = (bitLen >> 3); + rsap.Modulus = new byte [byteLen]; + Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen); + Array.Reverse (rsap.Modulus); + + RSA rsa = null; + try { + rsa = RSA.Create (); + rsa.ImportParameters (rsap); + } + catch (CryptographicException) { + // this may cause problem when this code is run under + // the SYSTEM identity on Windows (e.g. ASP.NET). See + // http://bugzilla.ximian.com/show_bug.cgi?id=77559 + CspParameters csp = new CspParameters (); + csp.Flags = CspProviderFlags.UseMachineKeyStore; + rsa = new RSACryptoServiceProvider (csp); + rsa.ImportParameters (rsap); + } + return rsa; + } + catch (Exception e) { + throw new CryptographicException ("Invalid blob.", e); + } + } + + // PRIVATEKEYBLOB + // PUBLICKEYBLOB + static public RSA FromCapiKeyBlob (byte [] blob) + { + return FromCapiKeyBlob (blob, 0); + } + + static public RSA FromCapiKeyBlob (byte [] blob, int offset) + { + if (blob == null) + throw new ArgumentNullException ("blob"); + if (offset >= blob.Length) + throw new ArgumentException ("blob is too small."); + + switch (blob [offset]) { + case 0x00: + // this could be a public key inside an header + // like "sn -e" would produce + if (blob [offset + 12] == 0x06) { + return FromCapiPublicKeyBlob (blob, offset + 12); + } + break; + case 0x06: + return FromCapiPublicKeyBlob (blob, offset); + case 0x07: + return FromCapiPrivateKeyBlob (blob, offset); + } + throw new CryptographicException ("Unknown blob format."); + } + + static public byte [] ToCapiPublicKeyBlob (RSA rsa) + { + RSAParameters p = rsa.ExportParameters (false); + int keyLength = p.Modulus.Length; // in bytes + byte [] blob = new byte [20 + keyLength]; + + blob [0] = 0x06; // Type - PUBLICKEYBLOB (0x06) + blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02) + // [2], [3] // RESERVED - Always 0 + blob [5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN) + blob [8] = 0x52; // Magic - RSA1 (ASCII in hex) + blob [9] = 0x53; + blob [10] = 0x41; + blob [11] = 0x31; + + byte [] bitlen = GetBytesLE (keyLength << 3); + blob [12] = bitlen [0]; // bitlen + blob [13] = bitlen [1]; + blob [14] = bitlen [2]; + blob [15] = bitlen [3]; + + // public exponent (DWORD) + int pos = 16; + int n = p.Exponent.Length; + while (n > 0) + blob [pos++] = p.Exponent [--n]; + // modulus + pos = 20; + byte [] part = p.Modulus; + int len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + return blob; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography/CryptoConvert.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography/CryptoConvert.cs.meta new file mode 100644 index 0000000..ba086fa --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography/CryptoConvert.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 40b945bb6ba518c4b8ce54b5e54a06b5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography/CryptoService.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography/CryptoService.cs new file mode 100644 index 0000000..5582b17 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography/CryptoService.cs @@ -0,0 +1,202 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.PE; +using MonoFN.Security.Cryptography; +using System; +using System.IO; +using System.Reflection; +using System.Runtime.Serialization; +using System.Security.Cryptography; + +namespace MonoFN.Cecil { + + // Most of this code has been adapted + // from Jeroen Frijters' fantastic work + // in IKVM.Reflection.Emit. Thanks! + + static class CryptoService { + + public static byte [] GetPublicKey (WriterParameters parameters) + { + using (var rsa = parameters.CreateRSA ()) { + var cspBlob = CryptoConvert.ToCapiPublicKeyBlob (rsa); + var publicKey = new byte [12 + cspBlob.Length]; + Buffer.BlockCopy (cspBlob, 0, publicKey, 12, cspBlob.Length); + // The first 12 bytes are documented at: + // http://msdn.microsoft.com/library/en-us/cprefadd/html/grfungethashfromfile.asp + // ALG_ID - Signature + publicKey [1] = 36; + // ALG_ID - Hash + publicKey [4] = 4; + publicKey [5] = 128; + // Length of Public Key (in bytes) + publicKey [8] = (byte)(cspBlob.Length >> 0); + publicKey [9] = (byte)(cspBlob.Length >> 8); + publicKey [10] = (byte)(cspBlob.Length >> 16); + publicKey [11] = (byte)(cspBlob.Length >> 24); + return publicKey; + } + } + + public static void StrongName (Stream stream, ImageWriter writer, WriterParameters parameters) + { + int strong_name_pointer; + + var strong_name = CreateStrongName (parameters, HashStream (stream, writer, out strong_name_pointer)); + PatchStrongName (stream, strong_name_pointer, strong_name); + } + + static void PatchStrongName (Stream stream, int strong_name_pointer, byte [] strong_name) + { + stream.Seek (strong_name_pointer, SeekOrigin.Begin); + stream.Write (strong_name, 0, strong_name.Length); + } + + static byte [] CreateStrongName (WriterParameters parameters, byte [] hash) + { + const string hash_algo = "SHA1"; + + using (var rsa = parameters.CreateRSA ()) { + var formatter = new RSAPKCS1SignatureFormatter (rsa); + formatter.SetHashAlgorithm (hash_algo); + + byte [] signature = formatter.CreateSignature (hash); + Array.Reverse (signature); + + return signature; + } + } + + static byte [] HashStream (Stream stream, ImageWriter writer, out int strong_name_pointer) + { + const int buffer_size = 8192; + + var text = writer.text; + var header_size = (int)writer.GetHeaderSize (); + var text_section_pointer = (int)text.PointerToRawData; + var strong_name_directory = writer.GetStrongNameSignatureDirectory (); + + if (strong_name_directory.Size == 0) + throw new InvalidOperationException (); + + strong_name_pointer = (int)(text_section_pointer + + (strong_name_directory.VirtualAddress - text.VirtualAddress)); + var strong_name_length = (int)strong_name_directory.Size; + + var sha1 = new SHA1Managed (); + var buffer = new byte [buffer_size]; + using (var crypto_stream = new CryptoStream (Stream.Null, sha1, CryptoStreamMode.Write)) { + stream.Seek (0, SeekOrigin.Begin); + CopyStreamChunk (stream, crypto_stream, buffer, header_size); + + stream.Seek (text_section_pointer, SeekOrigin.Begin); + CopyStreamChunk (stream, crypto_stream, buffer, (int)strong_name_pointer - text_section_pointer); + + stream.Seek (strong_name_length, SeekOrigin.Current); + CopyStreamChunk (stream, crypto_stream, buffer, (int)(stream.Length - (strong_name_pointer + strong_name_length))); + } + + return sha1.Hash; + } + + static void CopyStreamChunk (Stream stream, Stream dest_stream, byte [] buffer, int length) + { + while (length > 0) { + int read = stream.Read (buffer, 0, System.Math.Min (buffer.Length, length)); + dest_stream.Write (buffer, 0, read); + length -= read; + } + } + + public static byte [] ComputeHash (string file) + { + if (!File.Exists (file)) + return Empty.Array; + + using (var stream = new FileStream (file, FileMode.Open, FileAccess.Read, FileShare.Read)) + return ComputeHash (stream); + } + + public static byte [] ComputeHash (Stream stream) + { + const int buffer_size = 8192; + + var sha1 = new SHA1Managed (); + var buffer = new byte [buffer_size]; + + using (var crypto_stream = new CryptoStream (Stream.Null, sha1, CryptoStreamMode.Write)) + CopyStreamChunk (stream, crypto_stream, buffer, (int)stream.Length); + + return sha1.Hash; + } + + public static byte [] ComputeHash (params ByteBuffer [] buffers) + { + var sha1 = new SHA1Managed (); + + using (var crypto_stream = new CryptoStream (Stream.Null, sha1, CryptoStreamMode.Write)) { + for (int i = 0; i < buffers.Length; i++) { + crypto_stream.Write (buffers [i].buffer, 0, buffers [i].length); + } + } + + return sha1.Hash; + } + + public static Guid ComputeGuid (byte [] hash) + { + // From corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobContentId.cs + var guid = new byte [16]; + Buffer.BlockCopy (hash, 0, guid, 0, 16); + + // modify the guid data so it decodes to the form of a "random" guid ala rfc4122 + guid [7] = (byte)((guid [7] & 0x0f) | (4 << 4)); + guid [8] = (byte)((guid [8] & 0x3f) | (2 << 6)); + + return new Guid (guid); + } + } + + static partial class Mixin { + + public static RSA CreateRSA (this WriterParameters writer_parameters) + { + byte [] key; + string key_container; + + if (writer_parameters.StrongNameKeyBlob != null) + return CryptoConvert.FromCapiKeyBlob (writer_parameters.StrongNameKeyBlob); + + if (writer_parameters.StrongNameKeyContainer != null) + key_container = writer_parameters.StrongNameKeyContainer; + else if (!TryGetKeyContainer (writer_parameters.StrongNameKeyPair, out key, out key_container)) + return CryptoConvert.FromCapiKeyBlob (key); + + var parameters = new CspParameters { + Flags = CspProviderFlags.UseMachineKeyStore, + KeyContainerName = key_container, + KeyNumber = 2, + }; + + return new RSACryptoServiceProvider (parameters); + } + + static bool TryGetKeyContainer (ISerializable key_pair, out byte [] key, out string key_container) + { + var info = new SerializationInfo (typeof (StrongNameKeyPair), new FormatterConverter ()); + key_pair.GetObjectData (info, new StreamingContext ()); + + key = (byte [])info.GetValue ("_keyPairArray", typeof (byte [])); + key_container = info.GetString ("_keyPairContainer"); + return key_container != null; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography/CryptoService.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography/CryptoService.cs.meta new file mode 100644 index 0000000..13f5dd0 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography/CryptoService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c49085888b0afc047af3a03b536acdc2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.meta new file mode 100644 index 0000000..5cf9349 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e55573663ea38f0408c4c465c48fceac +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/Disposable.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/Disposable.cs new file mode 100644 index 0000000..37c3e93 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/Disposable.cs @@ -0,0 +1,45 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN { + + static class Disposable { + + public static Disposable Owned (T value) where T : class, IDisposable + { + return new Disposable (value, owned: true); + } + + public static Disposable NotOwned (T value) where T : class, IDisposable + { + return new Disposable (value, owned: false); + } + } + + struct Disposable : IDisposable where T : class, IDisposable { + + internal readonly T value; + readonly bool owned; + + public Disposable (T value, bool owned) + { + this.value = value; + this.owned = owned; + } + + public void Dispose () + { + if (value != null && owned) + value.Dispose (); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/Disposable.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/Disposable.cs.meta new file mode 100644 index 0000000..03c0571 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/Disposable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1aa8d86dc5c9323419e8b535d582fccf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/Empty.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/Empty.cs new file mode 100644 index 0000000..63a91e3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/Empty.cs @@ -0,0 +1,62 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Collections.Generic; +using System; + +namespace MonoFN { + + static class Empty { + + public static readonly T [] Array = new T [0]; + } + + class ArgumentNullOrEmptyException : ArgumentException { + + public ArgumentNullOrEmptyException (string paramName) + : base ("Argument null or empty", paramName) + { + } + } +} + +namespace MonoFN.Cecil { + + static partial class Mixin { + + public static bool IsNullOrEmpty (this T [] self) + { + return self == null || self.Length == 0; + } + + public static bool IsNullOrEmpty (this Collection self) + { + return self == null || self.size == 0; + } + + public static T [] Resize (this T [] self, int length) + { + Array.Resize (ref self, length); + return self; + } + + public static T [] Add (this T [] self, T item) + { + if (self == null) { + self = new [] { item }; + return self; + } + + self = self.Resize (self.Length + 1); + self [self.Length - 1] = item; + return self; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/Empty.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/Empty.cs.meta new file mode 100644 index 0000000..76bd34a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/Empty.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4662c38d1a951c24b89aeae3390acd39 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/MergeSort.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/MergeSort.cs new file mode 100644 index 0000000..4fde695 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/MergeSort.cs @@ -0,0 +1,66 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; +using System.Collections.Generic; + +namespace MonoFN { + + class MergeSort { + private readonly T [] elements; + private readonly T [] buffer; + private readonly IComparer comparer; + + private MergeSort (T [] elements, IComparer comparer) + { + this.elements = elements; + this.buffer = new T [elements.Length]; + Array.Copy (this.elements, this.buffer, elements.Length); + this.comparer = comparer; + } + + public static void Sort (T [] source, IComparer comparer) + { + Sort (source, 0, source.Length, comparer); + } + + public static void Sort (T [] source, int start, int length, IComparer comparer) + { + new MergeSort (source, comparer).Sort (start, length); + } + + private void Sort (int start, int length) + { + TopDownSplitMerge (this.buffer, this.elements, start, length); + } + + private void TopDownSplitMerge (T [] a, T [] b, int start, int end) + { + if (end - start < 2) + return; + + int middle = (end + start) / 2; + TopDownSplitMerge (b, a, start, middle); + TopDownSplitMerge (b, a, middle, end); + TopDownMerge (a, b, start, middle, end); + } + + private void TopDownMerge (T [] a, T [] b, int start, int middle, int end) + { + for (int i = start, j = middle, k = start; k < end; k++) { + if (i < middle && (j >= end || comparer.Compare (a [i], a [j]) <= 0)) { + b [k] = a [i++]; + } else { + b [k] = a [j++]; + } + } + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/MergeSort.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/MergeSort.cs.meta new file mode 100644 index 0000000..5e6a613 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/MergeSort.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3c35ecf3bff670b4c8b367ed632eee37 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/MonoFN.Cecil.asmdef b/Assets/FishNet/CodeGenerating/cecil-0.11.4/MonoFN.Cecil.asmdef new file mode 100644 index 0000000..c52c300 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/MonoFN.Cecil.asmdef @@ -0,0 +1,15 @@ +{ + "name": "FishNet.Codegen.Cecil", + "references": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": true, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/MonoFN.Cecil.asmdef.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/MonoFN.Cecil.asmdef.meta new file mode 100644 index 0000000..b5ca406 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/MonoFN.Cecil.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 23311a592bb0c5640b641143d87bf5b7 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/ProjectInfo.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/ProjectInfo.cs new file mode 100644 index 0000000..fe89134 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/ProjectInfo.cs @@ -0,0 +1,20 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// +// Licensed under the MIT/X11 license. +// + +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyProduct (Consts.AssemblyName)] +[assembly: AssemblyCopyright ("Copyright © 2008 - 2018 Jb Evain")] + +[assembly: ComVisible (false)] + +[assembly: AssemblyVersion ("0.11.4.0")] +[assembly: AssemblyFileVersion ("0.11.4.0")] +[assembly: AssemblyInformationalVersion ("0.11.4.0")] diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/ProjectInfo.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/ProjectInfo.cs.meta new file mode 100644 index 0000000..8fac323 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/ProjectInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 468fd5ab13cb01b4381e9c667167b2e0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/README.md b/Assets/FishNet/CodeGenerating/cecil-0.11.4/README.md new file mode 100644 index 0000000..c052e4c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/README.md @@ -0,0 +1,17 @@ +Cecil +===== + +Mono.Cecil is a library to generate and inspect programs and libraries in the ECMA CIL form. + +To put it simply, you can use Cecil to: + +* Analyze .NET binaries using a simple and powerful object model, without having to load assemblies to use Reflection. +* Modify .NET binaries, add new metadata structures and alter the IL code. + +Cecil has been around since 2004 and is [widely used](https://github.com/jbevain/cecil/wiki/Users) in the .NET community. If you're using Cecil, or depend on a framework, project, or product using it, please consider [sponsoring Cecil](https://github.com/sponsors/jbevain/). + +Read about the Cecil development on the [development log](http://cecil.pe). + +To discuss Cecil, the best place is the [mono-cecil](https://groups.google.com/group/mono-cecil) Google Group. + +Cecil is a project under the benevolent umbrella of the [.NET Foundation](http://www.dotnetfoundation.org/). diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/README.md.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/README.md.meta new file mode 100644 index 0000000..28851a6 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3bca5ad696a6f8e47b4651fa04b2cd96 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/cecil.snk b/Assets/FishNet/CodeGenerating/cecil-0.11.4/cecil.snk new file mode 100644 index 0000000..c0380d1 Binary files /dev/null and b/Assets/FishNet/CodeGenerating/cecil-0.11.4/cecil.snk differ diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/cecil.snk.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/cecil.snk.meta new file mode 100644 index 0000000..1ad02ba --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/cecil.snk.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 71693fd731252cb4a9bd4d8abf7846c0 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks.meta new file mode 100644 index 0000000..c070af3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c13f8d2e52ed89246b2f0dbc1f6ba1aa +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks.meta new file mode 100644 index 0000000..ab82924 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 15700071751bd1349a26170e9f6a1f14 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/AssemblyInfo.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/AssemblyInfo.cs new file mode 100644 index 0000000..4c82583 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/AssemblyInfo.cs @@ -0,0 +1,15 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +//[assembly: AssemblyTitle ("MonoFN.Cecil.Rocks")] + +[assembly: CLSCompliant (false)] diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/AssemblyInfo.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/AssemblyInfo.cs.meta new file mode 100644 index 0000000..a9fbe70 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/AssemblyInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f9ceb0221cb78b247b676d0ea5d57fc8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/DocCommentId.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/DocCommentId.cs new file mode 100644 index 0000000..783dbde --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/DocCommentId.cs @@ -0,0 +1,261 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; +using System.Collections.Generic; +using System.Text; + +namespace MonoFN.Cecil.Rocks { + + public class DocCommentId { + StringBuilder id; + + DocCommentId () + { + id = new StringBuilder (); + } + + void WriteField (FieldDefinition field) + { + WriteDefinition ('F', field); + } + + void WriteEvent (EventDefinition @event) + { + WriteDefinition ('E', @event); + } + + void WriteType (TypeDefinition type) + { + id.Append ('T').Append (':'); + WriteTypeFullName (type); + } + + void WriteMethod (MethodDefinition method) + { + WriteDefinition ('M', method); + + if (method.HasGenericParameters) { + id.Append ('`').Append ('`'); + id.Append (method.GenericParameters.Count); + } + + if (method.HasParameters) + WriteParameters (method.Parameters); + + if (IsConversionOperator (method)) + WriteReturnType (method); + } + + static bool IsConversionOperator (MethodDefinition self) + { + if (self == null) + throw new ArgumentNullException ("self"); + + return self.IsSpecialName + && (self.Name == "op_Explicit" || self.Name == "op_Implicit"); + } + + void WriteReturnType (MethodDefinition method) + { + id.Append ('~'); + WriteTypeSignature (method.ReturnType); + } + + void WriteProperty (PropertyDefinition property) + { + WriteDefinition ('P', property); + + if (property.HasParameters) + WriteParameters (property.Parameters); + } + + void WriteParameters (IList parameters) + { + id.Append ('('); + WriteList (parameters, p => WriteTypeSignature (p.ParameterType)); + id.Append (')'); + } + + void WriteTypeSignature (TypeReference type) + { + switch (type.MetadataType) { + case MetadataType.Array: + WriteArrayTypeSignature ((ArrayType)type); + break; + case MetadataType.ByReference: + WriteTypeSignature (((ByReferenceType)type).ElementType); + id.Append ('@'); + break; + case MetadataType.FunctionPointer: + WriteFunctionPointerTypeSignature ((FunctionPointerType)type); + break; + case MetadataType.GenericInstance: + WriteGenericInstanceTypeSignature ((GenericInstanceType)type); + break; + case MetadataType.Var: + id.Append ('`'); + id.Append (((GenericParameter)type).Position); + break; + case MetadataType.MVar: + id.Append ('`').Append ('`'); + id.Append (((GenericParameter)type).Position); + break; + case MetadataType.OptionalModifier: + WriteModiferTypeSignature ((OptionalModifierType)type, '!'); + break; + case MetadataType.RequiredModifier: + WriteModiferTypeSignature ((RequiredModifierType)type, '|'); + break; + case MetadataType.Pointer: + WriteTypeSignature (((PointerType)type).ElementType); + id.Append ('*'); + break; + default: + WriteTypeFullName (type); + break; + } + } + + void WriteGenericInstanceTypeSignature (GenericInstanceType type) + { + if (type.ElementType.IsTypeSpecification ()) + throw new NotSupportedException (); + + WriteTypeFullName (type.ElementType, stripGenericArity: true); + id.Append ('{'); + WriteList (type.GenericArguments, WriteTypeSignature); + id.Append ('}'); + } + + void WriteList (IList list, Action action) + { + for (int i = 0; i < list.Count; i++) { + if (i > 0) + id.Append (','); + + action (list [i]); + } + } + + void WriteModiferTypeSignature (IModifierType type, char id) + { + WriteTypeSignature (type.ElementType); + this.id.Append (id); + WriteTypeSignature (type.ModifierType); + } + + void WriteFunctionPointerTypeSignature (FunctionPointerType type) + { + id.Append ("=FUNC:"); + WriteTypeSignature (type.ReturnType); + + if (type.HasParameters) + WriteParameters (type.Parameters); + } + + void WriteArrayTypeSignature (ArrayType type) + { + WriteTypeSignature (type.ElementType); + + if (type.IsVector) { + id.Append ("[]"); + return; + } + + id.Append ("["); + + WriteList (type.Dimensions, dimension => { + if (dimension.LowerBound.HasValue) + id.Append (dimension.LowerBound.Value); + + id.Append (':'); + + if (dimension.UpperBound.HasValue) + id.Append (dimension.UpperBound.Value - (dimension.LowerBound.GetValueOrDefault () + 1)); + }); + + id.Append ("]"); + } + + void WriteDefinition (char id, IMemberDefinition member) + { + this.id.Append (id) + .Append (':'); + + WriteTypeFullName (member.DeclaringType); + this.id.Append ('.'); + WriteItemName (member.Name); + } + + void WriteTypeFullName (TypeReference type, bool stripGenericArity = false) + { + if (type.DeclaringType != null) { + WriteTypeFullName (type.DeclaringType); + id.Append ('.'); + } + + if (!string.IsNullOrEmpty (type.Namespace)) { + id.Append (type.Namespace); + id.Append ('.'); + } + + var name = type.Name; + + if (stripGenericArity) { + var index = name.LastIndexOf ('`'); + if (index > 0) + name = name.Substring (0, index); + } + + id.Append (name); + } + + void WriteItemName (string name) + { + id.Append (name.Replace ('.', '#').Replace ('<', '{').Replace ('>', '}')); + } + + public override string ToString () + { + return id.ToString (); + } + + public static string GetDocCommentId (IMemberDefinition member) + { + if (member == null) + throw new ArgumentNullException ("member"); + + var documentId = new DocCommentId (); + + switch (member.MetadataToken.TokenType) { + case TokenType.Field: + documentId.WriteField ((FieldDefinition)member); + break; + case TokenType.Method: + documentId.WriteMethod ((MethodDefinition)member); + break; + case TokenType.TypeDef: + documentId.WriteType ((TypeDefinition)member); + break; + case TokenType.Event: + documentId.WriteEvent ((EventDefinition)member); + break; + case TokenType.Property: + documentId.WriteProperty ((PropertyDefinition)member); + break; + default: + throw new NotSupportedException (member.FullName); + } + + return documentId.ToString (); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/DocCommentId.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/DocCommentId.cs.meta new file mode 100644 index 0000000..7cc833e --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/DocCommentId.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 370e40931a1b9cf478e08e66fd2a9eac +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/Functional.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/Functional.cs new file mode 100644 index 0000000..90c24ed --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/Functional.cs @@ -0,0 +1,41 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; +using System.Collections.Generic; + +namespace MonoFN.Cecil.Rocks { + + static class Functional { + + public static System.Func Y (System.Func, System.Func> f) + { + System.Func g = null; + g = f (a => g (a)); + return g; + } + + public static IEnumerable Prepend (this IEnumerable source, TSource element) + { + if (source == null) + throw new ArgumentNullException ("source"); + + return PrependIterator (source, element); + } + + static IEnumerable PrependIterator (IEnumerable source, TSource element) + { + yield return element; + + foreach (var item in source) + yield return item; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/Functional.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/Functional.cs.meta new file mode 100644 index 0000000..15f593e --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/Functional.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b481a942e0bdf8649b6b5b20db207190 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ILParser.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ILParser.cs new file mode 100644 index 0000000..9e814a6 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ILParser.cs @@ -0,0 +1,228 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.Cil; +using MonoFN.Collections.Generic; +using System; + +namespace MonoFN.Cecil.Rocks { + +#if UNITY_EDITOR + public +#endif + interface IILVisitor { + void OnInlineNone (OpCode opcode); + void OnInlineSByte (OpCode opcode, sbyte value); + void OnInlineByte (OpCode opcode, byte value); + void OnInlineInt32 (OpCode opcode, int value); + void OnInlineInt64 (OpCode opcode, long value); + void OnInlineSingle (OpCode opcode, float value); + void OnInlineDouble (OpCode opcode, double value); + void OnInlineString (OpCode opcode, string value); + void OnInlineBranch (OpCode opcode, int offset); + void OnInlineSwitch (OpCode opcode, int [] offsets); + void OnInlineVariable (OpCode opcode, VariableDefinition variable); + void OnInlineArgument (OpCode opcode, ParameterDefinition parameter); + void OnInlineSignature (OpCode opcode, CallSite callSite); + void OnInlineType (OpCode opcode, TypeReference type); + void OnInlineField (OpCode opcode, FieldReference field); + void OnInlineMethod (OpCode opcode, MethodReference method); + } + +#if UNITY_EDITOR + public +#endif + static class ILParser { + + class ParseContext { + public CodeReader Code { get; set; } + public int Position { get; set; } + public MetadataReader Metadata { get; set; } + public Collection Variables { get; set; } + public IILVisitor Visitor { get; set; } + } + + public static void Parse (MethodDefinition method, IILVisitor visitor) + { + if (method == null) + throw new ArgumentNullException ("method"); + if (visitor == null) + throw new ArgumentNullException ("visitor"); + if (!method.HasBody || !method.HasImage) + throw new ArgumentException (); + + method.Module.Read (method, (m, _) => { + ParseMethod (m, visitor); + return true; + }); + } + + static void ParseMethod (MethodDefinition method, IILVisitor visitor) + { + var context = CreateContext (method, visitor); + var code = context.Code; + + var flags = code.ReadByte (); + + switch (flags & 0x3) { + case 0x2: // tiny + int code_size = flags >> 2; + ParseCode (code_size, context); + break; + case 0x3: // fat + code.Advance (-1); + ParseFatMethod (context); + break; + default: + throw new NotSupportedException (); + } + + code.MoveBackTo (context.Position); + } + + static ParseContext CreateContext (MethodDefinition method, IILVisitor visitor) + { + var code = method.Module.Read (method, (_, reader) => reader.code); + var position = code.MoveTo (method); + + return new ParseContext { + Code = code, + Position = position, + Metadata = code.reader, + Visitor = visitor, + }; + } + + static void ParseFatMethod (ParseContext context) + { + var code = context.Code; + + code.Advance (4); + var code_size = code.ReadInt32 (); + var local_var_token = code.ReadToken (); + + if (local_var_token != MetadataToken.Zero) + context.Variables = code.ReadVariables (local_var_token); + + ParseCode (code_size, context); + } + + static void ParseCode (int code_size, ParseContext context) + { + var code = context.Code; + var metadata = context.Metadata; + var visitor = context.Visitor; + + var start = code.Position; + var end = start + code_size; + + while (code.Position < end) { + var il_opcode = code.ReadByte (); + var opcode = il_opcode != 0xfe + ? OpCodes.OneByteOpCode [il_opcode] + : OpCodes.TwoBytesOpCode [code.ReadByte ()]; + + switch (opcode.OperandType) { + case OperandType.InlineNone: + visitor.OnInlineNone (opcode); + break; + case OperandType.InlineSwitch: + var length = code.ReadInt32 (); + var branches = new int [length]; + for (int i = 0; i < length; i++) + branches [i] = code.ReadInt32 (); + visitor.OnInlineSwitch (opcode, branches); + break; + case OperandType.ShortInlineBrTarget: + visitor.OnInlineBranch (opcode, code.ReadSByte ()); + break; + case OperandType.InlineBrTarget: + visitor.OnInlineBranch (opcode, code.ReadInt32 ()); + break; + case OperandType.ShortInlineI: + if (opcode == OpCodes.Ldc_I4_S) + visitor.OnInlineSByte (opcode, code.ReadSByte ()); + else + visitor.OnInlineByte (opcode, code.ReadByte ()); + break; + case OperandType.InlineI: + visitor.OnInlineInt32 (opcode, code.ReadInt32 ()); + break; + case OperandType.InlineI8: + visitor.OnInlineInt64 (opcode, code.ReadInt64 ()); + break; + case OperandType.ShortInlineR: + visitor.OnInlineSingle (opcode, code.ReadSingle ()); + break; + case OperandType.InlineR: + visitor.OnInlineDouble (opcode, code.ReadDouble ()); + break; + case OperandType.InlineSig: + visitor.OnInlineSignature (opcode, code.GetCallSite (code.ReadToken ())); + break; + case OperandType.InlineString: + visitor.OnInlineString (opcode, code.GetString (code.ReadToken ())); + break; + case OperandType.ShortInlineArg: + visitor.OnInlineArgument (opcode, code.GetParameter (code.ReadByte ())); + break; + case OperandType.InlineArg: + visitor.OnInlineArgument (opcode, code.GetParameter (code.ReadInt16 ())); + break; + case OperandType.ShortInlineVar: + visitor.OnInlineVariable (opcode, GetVariable (context, code.ReadByte ())); + break; + case OperandType.InlineVar: + visitor.OnInlineVariable (opcode, GetVariable (context, code.ReadInt16 ())); + break; + case OperandType.InlineTok: + case OperandType.InlineField: + case OperandType.InlineMethod: + case OperandType.InlineType: + var member = metadata.LookupToken (code.ReadToken ()); + switch (member.MetadataToken.TokenType) { + case TokenType.TypeDef: + case TokenType.TypeRef: + case TokenType.TypeSpec: + visitor.OnInlineType (opcode, (TypeReference)member); + break; + case TokenType.Method: + case TokenType.MethodSpec: + visitor.OnInlineMethod (opcode, (MethodReference)member); + break; + case TokenType.Field: + visitor.OnInlineField (opcode, (FieldReference)member); + break; + case TokenType.MemberRef: + var field_ref = member as FieldReference; + if (field_ref != null) { + visitor.OnInlineField (opcode, field_ref); + break; + } + + var method_ref = member as MethodReference; + if (method_ref != null) { + visitor.OnInlineMethod (opcode, method_ref); + break; + } + + throw new InvalidOperationException (); + } + break; + } + } + } + + static VariableDefinition GetVariable (ParseContext context, int index) + { + return context.Variables [index]; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ILParser.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ILParser.cs.meta new file mode 100644 index 0000000..65cbdbe --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ILParser.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2916628c81c279046adb746612d6067b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/MethodBodyRocks.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/MethodBodyRocks.cs new file mode 100644 index 0000000..feaabbf --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/MethodBodyRocks.cs @@ -0,0 +1,411 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil.Cil; +using System; + +namespace MonoFN.Cecil.Rocks { + +#if UNITY_EDITOR + public +#endif + static class MethodBodyRocks { + + public static void SimplifyMacros (this MethodBody self) + { + if (self == null) + throw new ArgumentNullException ("self"); + + foreach (var instruction in self.Instructions) { + if (instruction.OpCode.OpCodeType != OpCodeType.Macro) + continue; + + switch (instruction.OpCode.Code) { + case Code.Ldarg_0: + ExpandMacro (instruction, OpCodes.Ldarg, self.GetParameter (0)); + break; + case Code.Ldarg_1: + ExpandMacro (instruction, OpCodes.Ldarg, self.GetParameter (1)); + break; + case Code.Ldarg_2: + ExpandMacro (instruction, OpCodes.Ldarg, self.GetParameter (2)); + break; + case Code.Ldarg_3: + ExpandMacro (instruction, OpCodes.Ldarg, self.GetParameter (3)); + break; + case Code.Ldloc_0: + ExpandMacro (instruction, OpCodes.Ldloc, self.Variables [0]); + break; + case Code.Ldloc_1: + ExpandMacro (instruction, OpCodes.Ldloc, self.Variables [1]); + break; + case Code.Ldloc_2: + ExpandMacro (instruction, OpCodes.Ldloc, self.Variables [2]); + break; + case Code.Ldloc_3: + ExpandMacro (instruction, OpCodes.Ldloc, self.Variables [3]); + break; + case Code.Stloc_0: + ExpandMacro (instruction, OpCodes.Stloc, self.Variables [0]); + break; + case Code.Stloc_1: + ExpandMacro (instruction, OpCodes.Stloc, self.Variables [1]); + break; + case Code.Stloc_2: + ExpandMacro (instruction, OpCodes.Stloc, self.Variables [2]); + break; + case Code.Stloc_3: + ExpandMacro (instruction, OpCodes.Stloc, self.Variables [3]); + break; + case Code.Ldarg_S: + instruction.OpCode = OpCodes.Ldarg; + break; + case Code.Ldarga_S: + instruction.OpCode = OpCodes.Ldarga; + break; + case Code.Starg_S: + instruction.OpCode = OpCodes.Starg; + break; + case Code.Ldloc_S: + instruction.OpCode = OpCodes.Ldloc; + break; + case Code.Ldloca_S: + instruction.OpCode = OpCodes.Ldloca; + break; + case Code.Stloc_S: + instruction.OpCode = OpCodes.Stloc; + break; + case Code.Ldc_I4_M1: + ExpandMacro (instruction, OpCodes.Ldc_I4, -1); + break; + case Code.Ldc_I4_0: + ExpandMacro (instruction, OpCodes.Ldc_I4, 0); + break; + case Code.Ldc_I4_1: + ExpandMacro (instruction, OpCodes.Ldc_I4, 1); + break; + case Code.Ldc_I4_2: + ExpandMacro (instruction, OpCodes.Ldc_I4, 2); + break; + case Code.Ldc_I4_3: + ExpandMacro (instruction, OpCodes.Ldc_I4, 3); + break; + case Code.Ldc_I4_4: + ExpandMacro (instruction, OpCodes.Ldc_I4, 4); + break; + case Code.Ldc_I4_5: + ExpandMacro (instruction, OpCodes.Ldc_I4, 5); + break; + case Code.Ldc_I4_6: + ExpandMacro (instruction, OpCodes.Ldc_I4, 6); + break; + case Code.Ldc_I4_7: + ExpandMacro (instruction, OpCodes.Ldc_I4, 7); + break; + case Code.Ldc_I4_8: + ExpandMacro (instruction, OpCodes.Ldc_I4, 8); + break; + case Code.Ldc_I4_S: + ExpandMacro (instruction, OpCodes.Ldc_I4, (int)(sbyte)instruction.Operand); + break; + case Code.Br_S: + instruction.OpCode = OpCodes.Br; + break; + case Code.Brfalse_S: + instruction.OpCode = OpCodes.Brfalse; + break; + case Code.Brtrue_S: + instruction.OpCode = OpCodes.Brtrue; + break; + case Code.Beq_S: + instruction.OpCode = OpCodes.Beq; + break; + case Code.Bge_S: + instruction.OpCode = OpCodes.Bge; + break; + case Code.Bgt_S: + instruction.OpCode = OpCodes.Bgt; + break; + case Code.Ble_S: + instruction.OpCode = OpCodes.Ble; + break; + case Code.Blt_S: + instruction.OpCode = OpCodes.Blt; + break; + case Code.Bne_Un_S: + instruction.OpCode = OpCodes.Bne_Un; + break; + case Code.Bge_Un_S: + instruction.OpCode = OpCodes.Bge_Un; + break; + case Code.Bgt_Un_S: + instruction.OpCode = OpCodes.Bgt_Un; + break; + case Code.Ble_Un_S: + instruction.OpCode = OpCodes.Ble_Un; + break; + case Code.Blt_Un_S: + instruction.OpCode = OpCodes.Blt_Un; + break; + case Code.Leave_S: + instruction.OpCode = OpCodes.Leave; + break; + } + } + } + + static void ExpandMacro (Instruction instruction, OpCode opcode, object operand) + { + instruction.OpCode = opcode; + instruction.Operand = operand; + } + + static void MakeMacro (Instruction instruction, OpCode opcode) + { + instruction.OpCode = opcode; + instruction.Operand = null; + } + + public static void Optimize (this MethodBody self) + { + if (self == null) + throw new ArgumentNullException ("self"); + + OptimizeLongs (self); + OptimizeMacros (self); + } + + static void OptimizeLongs (this MethodBody self) + { + for (var i = 0; i < self.Instructions.Count; i++) { + var instruction = self.Instructions [i]; + if (instruction.OpCode.Code != Code.Ldc_I8) + continue; + var l = (long)instruction.Operand; + if (l >= int.MaxValue || l <= int.MinValue) + continue; + ExpandMacro (instruction, OpCodes.Ldc_I4, (int)l); + self.Instructions.Insert (++i, Instruction.Create (OpCodes.Conv_I8)); + } + } + + public static void OptimizeMacros (this MethodBody self) + { + if (self == null) + throw new ArgumentNullException ("self"); + + var method = self.Method; + + foreach (var instruction in self.Instructions) { + int index; + switch (instruction.OpCode.Code) { + case Code.Ldarg: + index = ((ParameterDefinition)instruction.Operand).Index; + if (index == -1 && instruction.Operand == self.ThisParameter) + index = 0; + else if (method.HasThis) + index++; + + switch (index) { + case 0: + MakeMacro (instruction, OpCodes.Ldarg_0); + break; + case 1: + MakeMacro (instruction, OpCodes.Ldarg_1); + break; + case 2: + MakeMacro (instruction, OpCodes.Ldarg_2); + break; + case 3: + MakeMacro (instruction, OpCodes.Ldarg_3); + break; + default: + if (index < 256) + ExpandMacro (instruction, OpCodes.Ldarg_S, instruction.Operand); + break; + } + break; + case Code.Ldloc: + index = ((VariableDefinition)instruction.Operand).Index; + switch (index) { + case 0: + MakeMacro (instruction, OpCodes.Ldloc_0); + break; + case 1: + MakeMacro (instruction, OpCodes.Ldloc_1); + break; + case 2: + MakeMacro (instruction, OpCodes.Ldloc_2); + break; + case 3: + MakeMacro (instruction, OpCodes.Ldloc_3); + break; + default: + if (index < 256) + ExpandMacro (instruction, OpCodes.Ldloc_S, instruction.Operand); + break; + } + break; + case Code.Stloc: + index = ((VariableDefinition)instruction.Operand).Index; + switch (index) { + case 0: + MakeMacro (instruction, OpCodes.Stloc_0); + break; + case 1: + MakeMacro (instruction, OpCodes.Stloc_1); + break; + case 2: + MakeMacro (instruction, OpCodes.Stloc_2); + break; + case 3: + MakeMacro (instruction, OpCodes.Stloc_3); + break; + default: + if (index < 256) + ExpandMacro (instruction, OpCodes.Stloc_S, instruction.Operand); + break; + } + break; + case Code.Ldarga: + index = ((ParameterDefinition)instruction.Operand).Index; + if (index == -1 && instruction.Operand == self.ThisParameter) + index = 0; + else if (method.HasThis) + index++; + if (index < 256) + ExpandMacro (instruction, OpCodes.Ldarga_S, instruction.Operand); + break; + case Code.Ldloca: + if (((VariableDefinition)instruction.Operand).Index < 256) + ExpandMacro (instruction, OpCodes.Ldloca_S, instruction.Operand); + break; + case Code.Ldc_I4: + int i = (int)instruction.Operand; + switch (i) { + case -1: + MakeMacro (instruction, OpCodes.Ldc_I4_M1); + break; + case 0: + MakeMacro (instruction, OpCodes.Ldc_I4_0); + break; + case 1: + MakeMacro (instruction, OpCodes.Ldc_I4_1); + break; + case 2: + MakeMacro (instruction, OpCodes.Ldc_I4_2); + break; + case 3: + MakeMacro (instruction, OpCodes.Ldc_I4_3); + break; + case 4: + MakeMacro (instruction, OpCodes.Ldc_I4_4); + break; + case 5: + MakeMacro (instruction, OpCodes.Ldc_I4_5); + break; + case 6: + MakeMacro (instruction, OpCodes.Ldc_I4_6); + break; + case 7: + MakeMacro (instruction, OpCodes.Ldc_I4_7); + break; + case 8: + MakeMacro (instruction, OpCodes.Ldc_I4_8); + break; + default: + if (i >= -128 && i < 128) + ExpandMacro (instruction, OpCodes.Ldc_I4_S, (sbyte)i); + break; + } + break; + } + } + + OptimizeBranches (self); + } + + static void OptimizeBranches (MethodBody body) + { + ComputeOffsets (body); + + foreach (var instruction in body.Instructions) { + if (instruction.OpCode.OperandType != OperandType.InlineBrTarget) + continue; + + if (OptimizeBranch (instruction)) + ComputeOffsets (body); + } + } + + static bool OptimizeBranch (Instruction instruction) + { + var offset = ((Instruction)instruction.Operand).Offset - (instruction.Offset + instruction.OpCode.Size + 4); + if (!(offset >= -128 && offset <= 127)) + return false; + + switch (instruction.OpCode.Code) { + case Code.Br: + instruction.OpCode = OpCodes.Br_S; + break; + case Code.Brfalse: + instruction.OpCode = OpCodes.Brfalse_S; + break; + case Code.Brtrue: + instruction.OpCode = OpCodes.Brtrue_S; + break; + case Code.Beq: + instruction.OpCode = OpCodes.Beq_S; + break; + case Code.Bge: + instruction.OpCode = OpCodes.Bge_S; + break; + case Code.Bgt: + instruction.OpCode = OpCodes.Bgt_S; + break; + case Code.Ble: + instruction.OpCode = OpCodes.Ble_S; + break; + case Code.Blt: + instruction.OpCode = OpCodes.Blt_S; + break; + case Code.Bne_Un: + instruction.OpCode = OpCodes.Bne_Un_S; + break; + case Code.Bge_Un: + instruction.OpCode = OpCodes.Bge_Un_S; + break; + case Code.Bgt_Un: + instruction.OpCode = OpCodes.Bgt_Un_S; + break; + case Code.Ble_Un: + instruction.OpCode = OpCodes.Ble_Un_S; + break; + case Code.Blt_Un: + instruction.OpCode = OpCodes.Blt_Un_S; + break; + case Code.Leave: + instruction.OpCode = OpCodes.Leave_S; + break; + } + + return true; + } + + static void ComputeOffsets (MethodBody body) + { + var offset = 0; + foreach (var instruction in body.Instructions) { + instruction.Offset = offset; + offset += instruction.GetSize (); + } + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/MethodBodyRocks.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/MethodBodyRocks.cs.meta new file mode 100644 index 0000000..3067d55 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/MethodBodyRocks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0c47d4756ed8120429bad0887cff0366 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/MethodDefinitionRocks.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/MethodDefinitionRocks.cs new file mode 100644 index 0000000..040f795 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/MethodDefinitionRocks.cs @@ -0,0 +1,72 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil.Rocks { + +#if UNITY_EDITOR + public +#endif + static class MethodDefinitionRocks { + + public static MethodDefinition GetBaseMethod (this MethodDefinition self) + { + if (self == null) + throw new ArgumentNullException ("self"); + if (!self.IsVirtual) + return self; + if (self.IsNewSlot) + return self; + + var base_type = ResolveBaseType (self.DeclaringType); + while (base_type != null) { + var @base = GetMatchingMethod (base_type, self); + if (@base != null) + return @base; + + base_type = ResolveBaseType (base_type); + } + + return self; + } + + public static MethodDefinition GetOriginalBaseMethod (this MethodDefinition self) + { + if (self == null) + throw new ArgumentNullException ("self"); + + while (true) { + var @base = self.GetBaseMethod (); + if (@base == self) + return self; + + self = @base; + } + } + + static TypeDefinition ResolveBaseType (TypeDefinition type) + { + if (type == null) + return null; + + var base_type = type.BaseType; + if (base_type == null) + return null; + + return base_type.Resolve (); + } + + static MethodDefinition GetMatchingMethod (TypeDefinition type, MethodDefinition method) + { + return MetadataResolver.GetMethod (type.Methods, method); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/MethodDefinitionRocks.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/MethodDefinitionRocks.cs.meta new file mode 100644 index 0000000..af399f1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/MethodDefinitionRocks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7a5d7596633ddb043b9785a7fa7bfa6f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ModuleDefinitionRocks.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ModuleDefinitionRocks.cs new file mode 100644 index 0000000..7eeab2b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ModuleDefinitionRocks.cs @@ -0,0 +1,32 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MonoFN.Cecil.Rocks { + +#if UNITY_EDITOR + public +#endif + static class ModuleDefinitionRocks { + + public static IEnumerable GetAllTypes (this ModuleDefinition self) + { + if (self == null) + throw new ArgumentNullException ("self"); + + // it was fun to write, but we need a somewhat less convoluted implementation + return self.Types.SelectMany ( + Functional.Y> (f => type => type.NestedTypes.SelectMany (f).Prepend (type))); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ModuleDefinitionRocks.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ModuleDefinitionRocks.cs.meta new file mode 100644 index 0000000..682485f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ModuleDefinitionRocks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d90334a1cd085c047b9b8eddaa65cf09 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ParameterReferenceRocks.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ParameterReferenceRocks.cs new file mode 100644 index 0000000..30bbb70 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ParameterReferenceRocks.cs @@ -0,0 +1,11 @@ + +namespace MonoFN.Cecil.Rocks { + + public static class ParameterReferenceRocks { + + public static int GetSequence (this ParameterReference self) + { + return self.Index + 1; + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ParameterReferenceRocks.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ParameterReferenceRocks.cs.meta new file mode 100644 index 0000000..30bde11 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ParameterReferenceRocks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d276fe9256961b847b51a9bfc735384b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/SecurityDeclarationRocks.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/SecurityDeclarationRocks.cs new file mode 100644 index 0000000..bf22572 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/SecurityDeclarationRocks.cs @@ -0,0 +1,157 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +#if !NET_CORE + +using System; +using System.Security; +using SSP = System.Security.Permissions; + +namespace MonoFN.Cecil.Rocks { + +#if UNITY_EDITOR + public +#endif + static class SecurityDeclarationRocks { + + public static PermissionSet ToPermissionSet (this SecurityDeclaration self) + { + if (self == null) + throw new ArgumentNullException ("self"); + + PermissionSet set; + if (TryProcessPermissionSetAttribute (self, out set)) + return set; + + return CreatePermissionSet (self); + } + + static bool TryProcessPermissionSetAttribute (SecurityDeclaration declaration, out PermissionSet set) + { + set = null; + + if (!declaration.HasSecurityAttributes && declaration.SecurityAttributes.Count != 1) + return false; + + var security_attribute = declaration.SecurityAttributes [0]; + if (!security_attribute.AttributeType.IsTypeOf ("System.Security.Permissions", "PermissionSetAttribute")) + return false; + + var attribute = new SSP.PermissionSetAttribute ((SSP.SecurityAction)declaration.Action); + + var named_argument = security_attribute.Properties [0]; + string value = (string)named_argument.Argument.Value; + switch (named_argument.Name) { + case "XML": + attribute.XML = value; + break; + case "Name": + attribute.Name = value; + break; + default: + throw new NotImplementedException (named_argument.Name); + } + + set = attribute.CreatePermissionSet (); + return true; + } + + static PermissionSet CreatePermissionSet (SecurityDeclaration declaration) + { + var set = new PermissionSet (SSP.PermissionState.None); + + foreach (var attribute in declaration.SecurityAttributes) { + var permission = CreatePermission (declaration, attribute); + set.AddPermission (permission); + } + + return set; + } + + static IPermission CreatePermission (SecurityDeclaration declaration, SecurityAttribute attribute) + { + var attribute_type = Type.GetType (attribute.AttributeType.FullName); + if (attribute_type == null) + throw new ArgumentException ("attribute"); + + var security_attribute = CreateSecurityAttribute (attribute_type, declaration); + if (security_attribute == null) + throw new InvalidOperationException (); + + CompleteSecurityAttribute (security_attribute, attribute); + + return security_attribute.CreatePermission (); + } + + static void CompleteSecurityAttribute (SSP.SecurityAttribute security_attribute, SecurityAttribute attribute) + { + if (attribute.HasFields) + CompleteSecurityAttributeFields (security_attribute, attribute); + + if (attribute.HasProperties) + CompleteSecurityAttributeProperties (security_attribute, attribute); + } + + static void CompleteSecurityAttributeFields (SSP.SecurityAttribute security_attribute, SecurityAttribute attribute) + { + var type = security_attribute.GetType (); + + foreach (var named_argument in attribute.Fields) + type.GetField (named_argument.Name).SetValue (security_attribute, named_argument.Argument.Value); + } + + static void CompleteSecurityAttributeProperties (SSP.SecurityAttribute security_attribute, SecurityAttribute attribute) + { + var type = security_attribute.GetType (); + + foreach (var named_argument in attribute.Properties) + type.GetProperty (named_argument.Name).SetValue (security_attribute, named_argument.Argument.Value, null); + } + + static SSP.SecurityAttribute CreateSecurityAttribute (Type attribute_type, SecurityDeclaration declaration) + { + SSP.SecurityAttribute security_attribute; + try { + security_attribute = (SSP.SecurityAttribute)Activator.CreateInstance ( + attribute_type, new object [] { (SSP.SecurityAction)declaration.Action }); + } + catch (MissingMethodException) { + security_attribute = (SSP.SecurityAttribute)Activator.CreateInstance (attribute_type, new object [0]); + } + + return security_attribute; + } + + public static SecurityDeclaration ToSecurityDeclaration (this PermissionSet self, SecurityAction action, ModuleDefinition module) + { + if (self == null) + throw new ArgumentNullException ("self"); + if (module == null) + throw new ArgumentNullException ("module"); + + var declaration = new SecurityDeclaration (action); + + var attribute = new SecurityAttribute ( + module.TypeSystem.LookupType ("System.Security.Permissions", "PermissionSetAttribute")); + + attribute.Properties.Add ( + new CustomAttributeNamedArgument ( + "XML", + new CustomAttributeArgument ( + module.TypeSystem.String, self.ToXml ().ToString ()))); + + declaration.SecurityAttributes.Add (attribute); + + return declaration; + } + } +} + +#endif diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/SecurityDeclarationRocks.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/SecurityDeclarationRocks.cs.meta new file mode 100644 index 0000000..c659e1a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/SecurityDeclarationRocks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e9d89d937b712004089aafb3a1391907 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/TypeDefinitionRocks.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/TypeDefinitionRocks.cs new file mode 100644 index 0000000..7b7d78f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/TypeDefinitionRocks.cs @@ -0,0 +1,65 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MonoFN.Cecil.Rocks { + +#if UNITY_EDITOR + public +#endif + static class TypeDefinitionRocks { + + public static IEnumerable GetConstructors (this TypeDefinition self) + { + if (self == null) + throw new ArgumentNullException ("self"); + + if (!self.HasMethods) + return Empty.Array; + + return self.Methods.Where (method => method.IsConstructor); + } + + public static MethodDefinition GetStaticConstructor (this TypeDefinition self) + { + if (self == null) + throw new ArgumentNullException ("self"); + + if (!self.HasMethods) + return null; + + return self.GetConstructors ().FirstOrDefault (ctor => ctor.IsStatic); + } + + public static IEnumerable GetMethods (this TypeDefinition self) + { + if (self == null) + throw new ArgumentNullException ("self"); + + if (!self.HasMethods) + return Empty.Array; + + return self.Methods.Where (method => !method.IsConstructor); + } + + public static TypeReference GetEnumUnderlyingType (this TypeDefinition self) + { + if (self == null) + throw new ArgumentNullException ("self"); + if (!self.IsEnum) + throw new ArgumentException (); + + return Mixin.GetEnumUnderlyingType (self); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/TypeDefinitionRocks.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/TypeDefinitionRocks.cs.meta new file mode 100644 index 0000000..620eacd --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/TypeDefinitionRocks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b4d5393ac3307a84ca5babfbdcad9eea +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/TypeReferenceRocks.cs b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/TypeReferenceRocks.cs new file mode 100644 index 0000000..1fdd284 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/TypeReferenceRocks.cs @@ -0,0 +1,87 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// Copyright (c) 2008 - 2011 Novell, Inc. +// +// Licensed under the MIT/X11 license. +// + +using System; + +namespace MonoFN.Cecil.Rocks { + +#if UNITY_EDITOR + public +#endif + static class TypeReferenceRocks { + + public static ArrayType MakeArrayType (this TypeReference self) + { + return new ArrayType (self); + } + + public static ArrayType MakeArrayType (this TypeReference self, int rank) + { + if (rank == 0) + throw new ArgumentOutOfRangeException ("rank"); + + var array = new ArrayType (self); + + for (int i = 1; i < rank; i++) + array.Dimensions.Add (new ArrayDimension ()); + + return array; + } + + public static PointerType MakePointerType (this TypeReference self) + { + return new PointerType (self); + } + + public static ByReferenceType MakeByReferenceType (this TypeReference self) + { + return new ByReferenceType (self); + } + + public static OptionalModifierType MakeOptionalModifierType (this TypeReference self, TypeReference modifierType) + { + return new OptionalModifierType (modifierType, self); + } + + public static RequiredModifierType MakeRequiredModifierType (this TypeReference self, TypeReference modifierType) + { + return new RequiredModifierType (modifierType, self); + } + + public static GenericInstanceType MakeGenericInstanceType (this TypeReference self, params TypeReference [] arguments) + { + if (self == null) + throw new ArgumentNullException ("self"); + if (arguments == null) + throw new ArgumentNullException ("arguments"); + if (arguments.Length == 0) + throw new ArgumentException (); + if (self.GenericParameters.Count != arguments.Length) + throw new ArgumentException (); + + var instance = new GenericInstanceType (self, arguments.Length); + + foreach (var argument in arguments) + instance.GenericArguments.Add (argument); + + return instance; + } + + public static PinnedType MakePinnedType (this TypeReference self) + { + return new PinnedType (self); + } + + public static SentinelType MakeSentinelType (this TypeReference self) + { + return new SentinelType (self); + } + } +} diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/TypeReferenceRocks.cs.meta b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/TypeReferenceRocks.cs.meta new file mode 100644 index 0000000..0239366 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/TypeReferenceRocks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 25d089a650c8e0f45a6f9086aaf0004a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/DOCUMENTATION.txt b/Assets/FishNet/DOCUMENTATION.txt new file mode 100644 index 0000000..8318532 --- /dev/null +++ b/Assets/FishNet/DOCUMENTATION.txt @@ -0,0 +1,3 @@ +Please view our online documentation for the most up to date information: https://fish-networking.gitbook.io/docs/ + +Support is available on our discord. Please contact FirstGearGames#0001 @ https://discord.gg/Ta9HgDh4Hj \ No newline at end of file diff --git a/Assets/FishNet/DOCUMENTATION.txt.meta b/Assets/FishNet/DOCUMENTATION.txt.meta new file mode 100644 index 0000000..65996b4 --- /dev/null +++ b/Assets/FishNet/DOCUMENTATION.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b8eb79f9866e25342b4565ac8c981645 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos.meta b/Assets/FishNet/Demos.meta new file mode 100644 index 0000000..584457f --- /dev/null +++ b/Assets/FishNet/Demos.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ec29d7e4581430a4db9b879b3d950b84 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/FishNet.Demos.asmdef b/Assets/FishNet/Demos/FishNet.Demos.asmdef new file mode 100644 index 0000000..0350994 --- /dev/null +++ b/Assets/FishNet/Demos/FishNet.Demos.asmdef @@ -0,0 +1,15 @@ +{ + "name": "FishNet.Demos", + "references": [ + "GUID:7c88a4a7926ee5145ad2dfa06f454c67" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/FishNet.Demos.asmdef.meta b/Assets/FishNet/Demos/FishNet.Demos.asmdef.meta new file mode 100644 index 0000000..56eb043 --- /dev/null +++ b/Assets/FishNet/Demos/FishNet.Demos.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0bb35fc3181999548a4abea731e00e89 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Network LOD.meta b/Assets/FishNet/Demos/Network LOD.meta new file mode 100644 index 0000000..362f1e8 --- /dev/null +++ b/Assets/FishNet/Demos/Network LOD.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9842088b2698c394dbf5c7329faddb21 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Network LOD/Network LOD Demo README.txt b/Assets/FishNet/Demos/Network LOD/Network LOD Demo README.txt new file mode 100644 index 0000000..d54ba7b --- /dev/null +++ b/Assets/FishNet/Demos/Network LOD/Network LOD Demo README.txt @@ -0,0 +1,7 @@ +- Open scene. +- Select LOD Tester in scene and slide LOD Level. +- Press Play. + +Can only be tested as separate client and server. +LOD Level cannot be changed at runtime. +A level of 1 is the same as not using LOD. \ No newline at end of file diff --git a/Assets/FishNet/Demos/Network LOD/Network LOD Demo README.txt.meta b/Assets/FishNet/Demos/Network LOD/Network LOD Demo README.txt.meta new file mode 100644 index 0000000..839dcc1 --- /dev/null +++ b/Assets/FishNet/Demos/Network LOD/Network LOD Demo README.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c373771d26a3af04a84bd1afd5aabfe8 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Network LOD/Prefabs.meta b/Assets/FishNet/Demos/Network LOD/Prefabs.meta new file mode 100644 index 0000000..158c421 --- /dev/null +++ b/Assets/FishNet/Demos/Network LOD/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3ff6d7c186e86974b94a6f0902aef961 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Network LOD/Prefabs/NetworkLOD_Prefab.prefab b/Assets/FishNet/Demos/Network LOD/Prefabs/NetworkLOD_Prefab.prefab new file mode 100644 index 0000000..1213a4a --- /dev/null +++ b/Assets/FishNet/Demos/Network LOD/Prefabs/NetworkLOD_Prefab.prefab @@ -0,0 +1,218 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &4512293259955182959 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4512293259955182957} + - component: {fileID: 4512293259955182956} + - component: {fileID: 6667641716399555817} + - component: {fileID: -5271135124957689192} + m_Layer: 0 + m_Name: NetworkLOD_Prefab + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4512293259955182957 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4512293259955182959} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 3106622367778460228} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &4512293259955182956 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4512293259955182959} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + NetworkObserver: {fileID: 0} + k__BackingField: 2 + k__BackingField: 0 + _scenePathHash: 0 + k__BackingField: 0 + k__BackingField: 8082309187455287958 + _sceneNetworkObjects: + - {fileID: 4512293259955182956} + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: {fileID: 0} + _networkBehaviours: + - {fileID: 6667641716399555817} + - {fileID: -5271135124957689192} + k__BackingField: {fileID: 0} + k__BackingField: [] + _isNetworked: 1 + _isGlobal: 0 + _initializeOrder: 0 + _defaultDespawnType: 0 +--- !u!114 &6667641716399555817 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4512293259955182959} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9880e85651efd71469092ce519317f7b, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 4512293259955182956} + _networkObjectCache: {fileID: 4512293259955182956} + _renderer: {fileID: 8565678507340770901} +--- !u!114 &-5271135124957689192 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4512293259955182959} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a2836e36774ca1c4bbbee976e17b649c, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 1 + _addedNetworkObject: {fileID: 4512293259955182956} + _networkObjectCache: {fileID: 4512293259955182956} + _synchronizeParent: 0 + _packing: + Position: 1 + Rotation: 1 + Scale: 0 + _interpolation: 2 + _extrapolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + _clientAuthoritative: 1 + _sendToOwner: 1 + _synchronizePosition: 1 + _positionSnapping: + X: 0 + Y: 0 + Z: 0 + _synchronizeRotation: 1 + _rotationSnapping: + X: 0 + Y: 0 + Z: 0 + _synchronizeScale: 1 + _scaleSnapping: + X: 0 + Y: 0 + Z: 0 +--- !u!1 &7544314590428355348 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3106622367778460228} + - component: {fileID: 300268715198095524} + - component: {fileID: 8565678507340770901} + - component: {fileID: 6944568664394371648} + m_Layer: 0 + m_Name: Sphere + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3106622367778460228 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7544314590428355348} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4512293259955182957} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &300268715198095524 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7544314590428355348} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &8565678507340770901 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7544314590428355348} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!135 &6944568664394371648 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7544314590428355348} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} diff --git a/Assets/FishNet/Demos/Network LOD/Prefabs/NetworkLOD_Prefab.prefab.meta b/Assets/FishNet/Demos/Network LOD/Prefabs/NetworkLOD_Prefab.prefab.meta new file mode 100644 index 0000000..de7ba26 --- /dev/null +++ b/Assets/FishNet/Demos/Network LOD/Prefabs/NetworkLOD_Prefab.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 300370bdf7819da41937e0beac65b32c +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Network LOD/Scenes.meta b/Assets/FishNet/Demos/Network LOD/Scenes.meta new file mode 100644 index 0000000..1cfe0c6 --- /dev/null +++ b/Assets/FishNet/Demos/Network LOD/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 00e8cf37496d4954d876a194be050368 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Network LOD/Scenes/NetworkLOD_Demo.unity b/Assets/FishNet/Demos/Network LOD/Scenes/NetworkLOD_Demo.unity new file mode 100644 index 0000000..ba67c83 --- /dev/null +++ b/Assets/FishNet/Demos/Network LOD/Scenes/NetworkLOD_Demo.unity @@ -0,0 +1,1085 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!114 &370472794 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408886575219563} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 756c28cd3141c4140ae776188ee26729, type: 3} + m_Name: + m_EditorClassIdentifier: + NetworkTraffic: + _updateInteval: 1 + _updateClient: 0 + _updateServer: 1 +--- !u!114 &370472795 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408886575219563} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8bc8f0363ddc75946a958043c5e49a83, type: 3} + m_Name: + m_EditorClassIdentifier: + _color: {r: 0, g: 1, b: 0.8124151, a: 1} + _placement: 2 + _showOutgoing: 1 + _showIncoming: 0 +--- !u!114 &370472796 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408886575219563} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3fdaae44044276a49a52229c1597e33b, type: 3} + m_Name: + m_EditorClassIdentifier: + _updateOrder: 0 + _timingType: 0 + _allowTickDropping: 0 + _maximumFrameTicks: 2 + _tickRate: 30 + _pingInterval: 1 + _timingInterval: 2 + _physicsMode: 0 +--- !u!1 &442045874 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 442045875} + - component: {fileID: 442045876} + - component: {fileID: 442045877} + m_Layer: 0 + m_Name: --LOD Tester + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &442045875 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 442045874} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1453871496} + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &442045876 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 442045874} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + NetworkObserver: {fileID: 0} + k__BackingField: 0 + k__BackingField: 0 + _scenePathHash: 2359702710 + k__BackingField: 10134845970961547977 + k__BackingField: 0 + _sceneNetworkObjects: + - {fileID: 442045876} + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: {fileID: 0} + _networkBehaviours: + - {fileID: 442045877} + k__BackingField: {fileID: 0} + k__BackingField: [] + _isNetworked: 1 + _isGlobal: 0 + _initializeOrder: 0 + _defaultDespawnType: 0 +--- !u!114 &442045877 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 442045874} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3e21cdbd259430f4ea77e1f3a6c88fc4, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 442045876} + _networkObjectCache: {fileID: 442045876} + _prefab: {fileID: 4512293259955182956, guid: 300370bdf7819da41937e0beac65b32c, type: 3} + _observerManager: {fileID: 1776591436} + _lodLevel: 8 +--- !u!1 &584355000 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 584355002} + - component: {fileID: 584355001} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &584355001 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 584355000} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &584355002 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 584355000} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &585532990 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 585532993} + - component: {fileID: 585532992} + - component: {fileID: 585532991} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &585532991 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 585532990} + m_Enabled: 1 +--- !u!20 &585532992 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 585532990} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &585532993 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 585532990} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1453871495 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1453871496} + m_Layer: 0 + m_Name: GameObject + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1453871496 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1453871495} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -20} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 442045875} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1776591436 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408886575219563} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7d331f979d46e8e4a9fc90070c596d44, type: 3} + m_Name: + m_EditorClassIdentifier: + _useNetworkLod: 1 + _levelOfDetailDistances: + - 5 + - 10 + - 15 + - 30 + - 50 + - 70 + - 90 + - 100 + _updateHostVisibility: 1 + _defaultConditions: [] +--- !u!114 &1424052073952814568 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2480283714076101889} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.25490198, g: 0.25490198, b: 0.25490198, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 2b3dca501a9d8c8479dc71dd068aa8b8, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!1 &2480283714076101889 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 9139860295505404435} + - component: {fileID: 6745855428728600859} + - component: {fileID: 1424052073952814568} + m_Layer: 5 + m_Name: Indicator + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &3965864432884103650 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4808982256728133663} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.25490198, g: 0.25490198, b: 0.25490198, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 2b3dca501a9d8c8479dc71dd068aa8b8, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &4393252310954709616 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252310954709618} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 4393252310954709631} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 4393252311495835464} + m_MethodName: OnClick_Client + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!224 &4393252310954709617 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252310954709618} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 9139860295505404435} + m_Father: {fileID: 4393252311495835476} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 16, y: -96} + m_SizeDelta: {x: 256, y: 64} + m_Pivot: {x: 0, y: 1} +--- !u!1 &4393252310954709618 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4393252310954709617} + - component: {fileID: 4393252310954709630} + - component: {fileID: 4393252310954709631} + - component: {fileID: 4393252310954709616} + m_Layer: 5 + m_Name: Client + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!222 &4393252310954709630 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252310954709618} + m_CullTransparentMesh: 0 +--- !u!114 &4393252310954709631 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252310954709618} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 2d50394614f8feb4eb0567fb7618d84d, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &4393252311105513456 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311105513458} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 4393252311105513471} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 4393252311495835464} + m_MethodName: OnClick_Server + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!224 &4393252311105513457 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311105513458} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 7233259200116312561} + m_Father: {fileID: 4393252311495835476} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 16, y: -16} + m_SizeDelta: {x: 256, y: 64} + m_Pivot: {x: 0, y: 1} +--- !u!1 &4393252311105513458 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4393252311105513457} + - component: {fileID: 4393252311105513470} + - component: {fileID: 4393252311105513471} + - component: {fileID: 4393252311105513456} + m_Layer: 5 + m_Name: Server + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!222 &4393252311105513470 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311105513458} + m_CullTransparentMesh: 0 +--- !u!114 &4393252311105513471 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311105513458} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 1b187e63031bf7849b249c8212440c3b, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &4393252311495835464 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311495835465} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6d3606bfdac5a4743890fc1a5ecd8f24, type: 3} + m_Name: + m_EditorClassIdentifier: + _autoStartType: 1 + _stoppedColor: {r: 0.25490198, g: 0.25490198, b: 0.25490198, a: 1} + _changingColor: {r: 0.78431374, g: 0.6862745, b: 0, a: 1} + _startedColor: {r: 0, g: 0.5882353, b: 0.64705884, a: 1} + _serverIndicator: {fileID: 3965864432884103650} + _clientIndicator: {fileID: 1424052073952814568} +--- !u!1 &4393252311495835465 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4393252311495835476} + - component: {fileID: 4393252311495835464} + - component: {fileID: 4393252311495835477} + - component: {fileID: 4393252311495835478} + - component: {fileID: 4393252311495835479} + m_Layer: 5 + m_Name: NetworkHudCanvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4393252311495835476 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311495835465} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_Children: + - {fileID: 4393252311105513457} + - {fileID: 4393252310954709617} + m_Father: {fileID: 7443408886575219561} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!223 &4393252311495835477 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311495835465} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!114 &4393252311495835478 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311495835465} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 1 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 1920, y: 1080} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0.5 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 +--- !u!114 &4393252311495835479 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311495835465} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!1 &4808982256728133663 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7233259200116312561} + - component: {fileID: 5104387649625413016} + - component: {fileID: 3965864432884103650} + m_Layer: 5 + m_Name: Indicator + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!222 &5104387649625413016 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4808982256728133663} + m_CullTransparentMesh: 0 +--- !u!222 &6745855428728600859 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2480283714076101889} + m_CullTransparentMesh: 0 +--- !u!224 &7233259200116312561 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4808982256728133663} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -1} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4393252311105513457} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &7443408886575219556 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408886575219563} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 211a9f6ec51ddc14f908f5acc0cd0423, type: 3} + m_Name: + m_EditorClassIdentifier: + _playerPrefab: {fileID: 4512293259955182956, guid: 300370bdf7819da41937e0beac65b32c, + type: 3} + _addToDefaultScene: 1 + Spawns: [] +--- !u!4 &7443408886575219561 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408886575219563} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4393252311495835476} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &7443408886575219562 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408886575219563} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d2c95dfde7d73b54dbbdc23155d35d36, type: 3} + m_Name: + m_EditorClassIdentifier: + _logging: {fileID: 0} + _spawnablePrefabs: {fileID: 11400000, guid: 3a54436bdb916194f99da0d17231e617, type: 2} + _refreshDefaultPrefabs: 1 + _runInBackground: 1 + _dontDestroyOnLoad: 1 + _objectPool: {fileID: 0} + _persistence: 0 +--- !u!1 &7443408886575219563 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7443408886575219561} + - component: {fileID: 7443408886575219562} + - component: {fileID: 1776591436} + - component: {fileID: 7443408886575219556} + - component: {fileID: 370472794} + - component: {fileID: 370472795} + - component: {fileID: 370472796} + m_Layer: 0 + m_Name: NetworkManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &9139860295505404435 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2480283714076101889} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -1} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4393252310954709617} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} diff --git a/Assets/FishNet/Demos/Network LOD/Scenes/NetworkLOD_Demo.unity.meta b/Assets/FishNet/Demos/Network LOD/Scenes/NetworkLOD_Demo.unity.meta new file mode 100644 index 0000000..d5f8856 --- /dev/null +++ b/Assets/FishNet/Demos/Network LOD/Scenes/NetworkLOD_Demo.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 961b96636e4c7644584dc8dfd0d0aad6 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Network LOD/Scripts.meta b/Assets/FishNet/Demos/Network LOD/Scripts.meta new file mode 100644 index 0000000..3be5703 --- /dev/null +++ b/Assets/FishNet/Demos/Network LOD/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d7351117344cad34c815771255551da8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Network LOD/Scripts/MoveRandomly.cs b/Assets/FishNet/Demos/Network LOD/Scripts/MoveRandomly.cs new file mode 100644 index 0000000..5a6017c --- /dev/null +++ b/Assets/FishNet/Demos/Network LOD/Scripts/MoveRandomly.cs @@ -0,0 +1,53 @@ +using FishNet.Object; +using UnityEngine; + +namespace FishNet.Demo.NetworkLod +{ + + public class MoveRandomly : NetworkBehaviour + { + //Colors green for client. + [SerializeField] + private Renderer _renderer; + + //Time to move to new position. + private const float _moveRate = 3f; + //Maximum range for new position. + private const float _range = 10f; + //Position to move towards. + private Vector3 _goal; + //Position at spawn. + private Vector3 _start; + + private void Update() + { + //Client should not move these. + if (base.IsClientOnly) + return; + //Server shouldn't move client one. + if (base.Owner.IsValid) + return; + + transform.position = Vector3.MoveTowards(transform.position, _goal, _moveRate * Time.deltaTime); + if (transform.position == _goal) + RandomizeGoal(); + } + + public override void OnStartNetwork() + { + base.OnStartNetwork(); + _start = transform.position; + RandomizeGoal(); + + if (_renderer != null && base.Owner.IsActive) + _renderer.material.color = Color.green; + } + + private void RandomizeGoal() + { + _goal = _start + (Random.insideUnitSphere * _range); + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/Network LOD/Scripts/MoveRandomly.cs.meta b/Assets/FishNet/Demos/Network LOD/Scripts/MoveRandomly.cs.meta new file mode 100644 index 0000000..bd16c3d --- /dev/null +++ b/Assets/FishNet/Demos/Network LOD/Scripts/MoveRandomly.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9880e85651efd71469092ce519317f7b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Network LOD/Scripts/NetworkLodTester.cs b/Assets/FishNet/Demos/Network LOD/Scripts/NetworkLodTester.cs new file mode 100644 index 0000000..26958a7 --- /dev/null +++ b/Assets/FishNet/Demos/Network LOD/Scripts/NetworkLodTester.cs @@ -0,0 +1,51 @@ +using FishNet.Managing.Observing; +using FishNet.Object; +using System.Collections.Generic; +using UnityEngine; + + +namespace FishNet.Demo.NetworkLod +{ + + public class NetworkLodTester : NetworkBehaviour + { + [SerializeField] + private NetworkObject _prefab; + [SerializeField] + private ObserverManager _observerManager; + [Range(1, 8)] + [SerializeField] + private byte _lodLevel = 8; + + private const int _count = 500; + private const float _xyRange = 15f; + private const float _zRange = 100f; + + private void Awake() + { + List distances = _observerManager.GetLevelOfDetailDistances(); + while (distances.Count > _lodLevel) + distances.RemoveAt(distances.Count - 1); + } + + public override void OnStartServer() + { + base.OnStartServer(); + + for (int i = 0; i < _count; i++) + { + float x = Random.Range(-_xyRange, _xyRange); + float y = Random.Range(-_xyRange, _xyRange); + float z = Random.Range(0f, _zRange); + + Vector3 position = new Vector3(x, y, z); + NetworkObject obj = Instantiate(_prefab, position, Quaternion.identity); + obj.name = $"Obj {i.ToString("0000")}"; + base.Spawn(obj); + } + + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/Network LOD/Scripts/NetworkLodTester.cs.meta b/Assets/FishNet/Demos/Network LOD/Scripts/NetworkLodTester.cs.meta new file mode 100644 index 0000000..b83bb07 --- /dev/null +++ b/Assets/FishNet/Demos/Network LOD/Scripts/NetworkLodTester.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3e21cdbd259430f4ea77e1f3a6c88fc4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example.meta b/Assets/FishNet/Example.meta new file mode 100644 index 0000000..ad5b2cf --- /dev/null +++ b/Assets/FishNet/Example.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 79bbd25806bd92d4c90cd527b542fd2f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All.meta b/Assets/FishNet/Example/All.meta new file mode 100644 index 0000000..1b57a73 --- /dev/null +++ b/Assets/FishNet/Example/All.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3b7816b0f21ed6947b4bdfa22c415e42 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Authenticator.meta b/Assets/FishNet/Example/All/Authenticator.meta new file mode 100644 index 0000000..2152fd0 --- /dev/null +++ b/Assets/FishNet/Example/All/Authenticator.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: faf238f4c9ee3f24eaf0485ba10b8baa +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Authenticator/Authenticator.unity b/Assets/FishNet/Example/All/Authenticator/Authenticator.unity new file mode 100644 index 0000000..dd4d190 --- /dev/null +++ b/Assets/FishNet/Example/All/Authenticator/Authenticator.unity @@ -0,0 +1,375 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.37311953, g: 0.38074014, b: 0.3587274, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &279669268 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 279669271} + - component: {fileID: 279669270} + - component: {fileID: 279669269} + m_Layer: 0 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &279669269 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 279669268} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} + m_Name: + m_EditorClassIdentifier: + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!114 &279669270 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 279669268} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_FirstSelected: {fileID: 0} + m_sendNavigationEvents: 1 + m_DragThreshold: 10 +--- !u!4 &279669271 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 279669268} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1001 &1689326519 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_Pivot.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_Pivot.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_RootOrder + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_AnchorMax.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_AnchorMin.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_SizeDelta.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_AnchoredPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058994, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: _autoStartType + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058995, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_Name + value: NetworkHudCanvas + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3} +--- !u!114 &1759771918 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408886491481971} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 226e4eaf2fa685f48bdc3dfaa87c1453, type: 3} + m_Name: + m_EditorClassIdentifier: + _password: HelloWorld +--- !u!4 &7443408886491481969 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408886491481971} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &7443408886491481970 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408886491481971} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d2c95dfde7d73b54dbbdc23155d35d36, type: 3} + m_Name: + m_EditorClassIdentifier: + _logging: {fileID: 0} + _spawnablePrefabs: {fileID: 11400000, guid: ec64eb18c93ab344892891f33edbf82a, type: 2} + _runInBackground: 1 + _dontDestroyOnLoad: 1 + _persistence: 0 +--- !u!1 &7443408886491481971 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7443408886491481969} + - component: {fileID: 7443408886491481970} + - component: {fileID: 1759771918} + m_Layer: 0 + m_Name: NetworkManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 diff --git a/Assets/FishNet/Example/All/Authenticator/Authenticator.unity.meta b/Assets/FishNet/Example/All/Authenticator/Authenticator.unity.meta new file mode 100644 index 0000000..13af204 --- /dev/null +++ b/Assets/FishNet/Example/All/Authenticator/Authenticator.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0bc02b628363de5499d5e7c00bd63b1b +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Authenticator/Scripts.meta b/Assets/FishNet/Example/All/Authenticator/Scripts.meta new file mode 100644 index 0000000..fbd38e5 --- /dev/null +++ b/Assets/FishNet/Example/All/Authenticator/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a21030a9aa6adbe409485ca049602ba6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Authenticator/Scripts/Broadcasts.cs b/Assets/FishNet/Example/All/Authenticator/Scripts/Broadcasts.cs new file mode 100644 index 0000000..9991b66 --- /dev/null +++ b/Assets/FishNet/Example/All/Authenticator/Scripts/Broadcasts.cs @@ -0,0 +1,21 @@ + +using FishNet.Broadcast; + +namespace FishNet.Example.Authenticating +{ + public struct HostPasswordBroadcast : IBroadcast + { + public string Password; + } + + public struct PasswordBroadcast : IBroadcast + { + public string Password; + } + + public struct ResponseBroadcast : IBroadcast + { + public bool Passed; + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Example/All/Authenticator/Scripts/Broadcasts.cs.meta b/Assets/FishNet/Example/All/Authenticator/Scripts/Broadcasts.cs.meta new file mode 100644 index 0000000..c2d9e65 --- /dev/null +++ b/Assets/FishNet/Example/All/Authenticator/Scripts/Broadcasts.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d26bb0c99070e9b49bc8632dc0b68214 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Authenticator/Scripts/HostAuthenticator.cs b/Assets/FishNet/Example/All/Authenticator/Scripts/HostAuthenticator.cs new file mode 100644 index 0000000..ea250f5 --- /dev/null +++ b/Assets/FishNet/Example/All/Authenticator/Scripts/HostAuthenticator.cs @@ -0,0 +1,152 @@ +using FishNet.Connection; +using FishNet.Example.Authenticating; +using FishNet.Managing; +using FishNet.Transporting; +using System; +using System.Security.Cryptography; +using System.Text; +using UnityEngine; + +namespace FishNet.Authenticating +{ + + /// + /// This authenticator is an example of how to let host bypass the authentication process. + /// When checking to authenticate on the client side call AuthenticateAsHost, and if returned true skip normal authentication. + /// + public abstract class HostAuthenticator : Authenticator + { + #region Serialized. + /// + /// True to enable use of AuthenticateAsHost. + /// + [Tooltip("True to enable use of AuthenticateAsHost.")] + [SerializeField] + private bool _allowHostAuthentication; + /// + /// Sets if to allow host authentication. + /// + /// + public void SetAllowHostAuthentication(bool value) => _allowHostAuthentication = value; + /// + /// Returns if AllowHostAuthentication is enabled. + /// + /// + public bool GetAllowHostAuthentication() => _allowHostAuthentication; + #endregion + + #region Private. + /// + /// A random hash which only exist if the server is started. + /// + private static string _hostHash = string.Empty; + #endregion + + /// + /// Initializes this script for use. + /// + /// + public override void InitializeOnce(NetworkManager networkManager) + { + base.InitializeOnce(networkManager); + //Listen for connection state of local server to set hash. + base.NetworkManager.ServerManager.OnServerConnectionState += ServerManager_OnServerConnectionState; + //Listen for broadcast from client. Be sure to set requireAuthentication to false. + base.NetworkManager.ServerManager.RegisterBroadcast(OnHostPasswordBroadcast, false); + } + + /// + /// Called after the local server connection state changes. + /// + private void ServerManager_OnServerConnectionState(ServerConnectionStateArgs obj) + { + int length = (obj.ConnectionState == LocalConnectionState.Started) ? 25 : 0; + SetHostHash(length); + } + + /// + /// Received on server when a client sends the password broadcast message. + /// + /// Connection sending broadcast. + /// + private void OnHostPasswordBroadcast(NetworkConnection conn, HostPasswordBroadcast hpb) + { + //Not accepting host authentications. This could be an attack. + if (!_allowHostAuthentication) + { + conn.Disconnect(true); + return; + } + /* If client is already authenticated this could be an attack. Connections + * are removed when a client disconnects so there is no reason they should + * already be considered authenticated. */ + if (conn.Authenticated) + { + conn.Disconnect(true); + return; + } + + bool correctPassword = (hpb.Password == _hostHash); + OnHostAuthenticationResult(conn, correctPassword); + } + + /// + /// Called after handling a host authentication result. + /// + /// Connection authenticating. + /// True if authentication passed. + protected abstract void OnHostAuthenticationResult(NetworkConnection conn, bool authenticated); + + /// + /// Sets a host hash of length. + /// + /// https://stackoverflow.com/questions/32932679/using-rngcryptoserviceprovider-to-generate-random-string + private void SetHostHash(int length) + { + if (length <= 0) + { + _hostHash = string.Empty; + } + else + { + const string charPool = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()"; + StringBuilder result = new StringBuilder(); + using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider()) + { + byte[] uintBuffer = new byte[sizeof(uint)]; + while (length-- > 0) + { + rng.GetBytes(uintBuffer); + uint num = BitConverter.ToUInt32(uintBuffer, 0); + result.Append(charPool[(int)(num % (uint)charPool.Length)]); + } + } + + _hostHash = result.ToString(); + } + } + + /// + /// Returns true if authentication was sent as host. + /// + /// + protected bool AuthenticateAsHost() + { + if (!_allowHostAuthentication) + return false; + if (_hostHash == string.Empty) + return false; + + HostPasswordBroadcast hpb = new HostPasswordBroadcast() + { + Password = _hostHash, + }; + + base.NetworkManager.ClientManager.Broadcast(hpb); + return true; + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Example/All/Authenticator/Scripts/HostAuthenticator.cs.meta b/Assets/FishNet/Example/All/Authenticator/Scripts/HostAuthenticator.cs.meta new file mode 100644 index 0000000..d26ea1d --- /dev/null +++ b/Assets/FishNet/Example/All/Authenticator/Scripts/HostAuthenticator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c7497d751bb68f444b4343e3edc28e39 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Authenticator/Scripts/PasswordAuthenticator.cs b/Assets/FishNet/Example/All/Authenticator/Scripts/PasswordAuthenticator.cs new file mode 100644 index 0000000..6bc7627 --- /dev/null +++ b/Assets/FishNet/Example/All/Authenticator/Scripts/PasswordAuthenticator.cs @@ -0,0 +1,132 @@ +using FishNet.Authenticating; +using FishNet.Connection; +using FishNet.Managing; +using FishNet.Managing.Logging; +using FishNet.Transporting; +using System; +using UnityEngine; + +namespace FishNet.Example.Authenticating +{ + /// + /// This is an example of a password authenticator. + /// Never send passwords without encryption. + /// + public class PasswordAuthenticator : HostAuthenticator + { + #region Public. + /// + /// Called when authenticator has concluded a result for a connection. Boolean is true if authentication passed, false if failed. + /// Server listens for this event automatically. + /// + public override event Action OnAuthenticationResult; + #endregion + + #region Serialized. + /// + /// Password to authenticate. + /// + [Tooltip("Password to authenticate.")] + [SerializeField] + private string _password = "HelloWorld"; + #endregion + + public override void InitializeOnce(NetworkManager networkManager) + { + base.InitializeOnce(networkManager); + + //Listen for connection state change as client. + base.NetworkManager.ClientManager.OnClientConnectionState += ClientManager_OnClientConnectionState; + //Listen for broadcast from client. Be sure to set requireAuthentication to false. + base.NetworkManager.ServerManager.RegisterBroadcast(OnPasswordBroadcast, false); + //Listen to response from server. + base.NetworkManager.ClientManager.RegisterBroadcast(OnResponseBroadcast); + } + + /// + /// Called when a connection state changes for the local client. + /// + private void ClientManager_OnClientConnectionState(ClientConnectionStateArgs args) + { + /* If anything but the started state then exit early. + * Only try to authenticate on started state. The server + * doesn't have to send an authentication request before client + * can authenticate, that is entirely optional and up to you. In this + * example the client tries to authenticate soon as they connect. */ + if (args.ConnectionState != LocalConnectionState.Started) + return; + //Authentication was sent as host, no need to authenticate normally. + if (AuthenticateAsHost()) + return; + + PasswordBroadcast pb = new PasswordBroadcast() + { + Password = _password + }; + + base.NetworkManager.ClientManager.Broadcast(pb); + } + + + /// + /// Received on server when a client sends the password broadcast message. + /// + /// Connection sending broadcast. + /// + private void OnPasswordBroadcast(NetworkConnection conn, PasswordBroadcast pb) + { + /* If client is already authenticated this could be an attack. Connections + * are removed when a client disconnects so there is no reason they should + * already be considered authenticated. */ + if (conn.Authenticated) + { + conn.Disconnect(true); + return; + } + + bool correctPassword = (pb.Password == _password); + SendAuthenticationResponse(conn, correctPassword); + /* Invoke result. This is handled internally to complete the connection or kick client. + * It's important to call this after sending the broadcast so that the broadcast + * makes it out to the client before the kick. */ + OnAuthenticationResult?.Invoke(conn, correctPassword); + } + + /// + /// Received on client after server sends an authentication response. + /// + /// + private void OnResponseBroadcast(ResponseBroadcast rb) + { + string result = (rb.Passed) ? "Authentication complete." : "Authenitcation failed."; + NetworkManager.Log(result); + } + + /// + /// Sends an authentication result to a connection. + /// + private void SendAuthenticationResponse(NetworkConnection conn, bool authenticated) + { + /* Tell client if they authenticated or not. This is + * entirely optional but does demonstrate that you can send + * broadcasts to client on pass or fail. */ + ResponseBroadcast rb = new ResponseBroadcast() + { + Passed = authenticated + }; + base.NetworkManager.ServerManager.Broadcast(conn, rb, false); + } + /// + /// Called after handling a host authentication result. + /// + /// Connection authenticating. + /// True if authentication passed. + protected override void OnHostAuthenticationResult(NetworkConnection conn, bool authenticated) + { + SendAuthenticationResponse(conn, authenticated); + OnAuthenticationResult?.Invoke(conn, authenticated); + } + } + + +} diff --git a/Assets/FishNet/Example/All/Authenticator/Scripts/PasswordAuthenticator.cs.meta b/Assets/FishNet/Example/All/Authenticator/Scripts/PasswordAuthenticator.cs.meta new file mode 100644 index 0000000..135ee06 --- /dev/null +++ b/Assets/FishNet/Example/All/Authenticator/Scripts/PasswordAuthenticator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 226e4eaf2fa685f48bdc3dfaa87c1453 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/CustomSyncType.meta b/Assets/FishNet/Example/All/CustomSyncType.meta new file mode 100644 index 0000000..b828a51 --- /dev/null +++ b/Assets/FishNet/Example/All/CustomSyncType.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f8bfe7bb849f1524fb53a1029c93cddc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/CustomSyncType/Component State Sync.meta b/Assets/FishNet/Example/All/CustomSyncType/Component State Sync.meta new file mode 100644 index 0000000..e8dbf47 --- /dev/null +++ b/Assets/FishNet/Example/All/CustomSyncType/Component State Sync.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bca1763a5b81adc4f8a467bb085aa78a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/CustomSyncType/Component State Sync/AMonoScript.cs b/Assets/FishNet/Example/All/CustomSyncType/Component State Sync/AMonoScript.cs new file mode 100644 index 0000000..8a62bed --- /dev/null +++ b/Assets/FishNet/Example/All/CustomSyncType/Component State Sync/AMonoScript.cs @@ -0,0 +1,17 @@ +using UnityEngine; + +namespace FishNet.Example.ComponentStateSync +{ + + + public class AMonoScript : MonoBehaviour + { + + private void Start() + { + //Start is here to show enabled toggle within inspector. + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Example/All/CustomSyncType/Component State Sync/AMonoScript.cs.meta b/Assets/FishNet/Example/All/CustomSyncType/Component State Sync/AMonoScript.cs.meta new file mode 100644 index 0000000..703535d --- /dev/null +++ b/Assets/FishNet/Example/All/CustomSyncType/Component State Sync/AMonoScript.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a5821b28e5b90ef44b4910a640482c15 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/CustomSyncType/Component State Sync/ComponentStateSync.cs b/Assets/FishNet/Example/All/CustomSyncType/Component State Sync/ComponentStateSync.cs new file mode 100644 index 0000000..9ef6626 --- /dev/null +++ b/Assets/FishNet/Example/All/CustomSyncType/Component State Sync/ComponentStateSync.cs @@ -0,0 +1,155 @@ +using FishNet.Documenting; +using FishNet.Managing.Logging; +using FishNet.Object.Synchronizing; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Example.ComponentStateSync +{ + + + /// + /// It's very important to exclude this from codegen. + /// However, whichever value you are synchronizing must not be excluded. This is why the value is outside the StructySync class. + /// + public class ComponentStateSync : SyncBase, ICustomSync where T : MonoBehaviour + { + #region Public. + /// + /// Gets or Sets the enabled state for Component. + /// + public bool Enabled + { + get => (Component == null) ? false : GetState(); + set => SetState(value); + } + /// + /// Component to state sync. + /// + public T Component { get; private set; } + /// + /// Delegate signature for when the component changes. + /// + public delegate void StateChanged(T component, bool prevState, bool nextState, bool asServer); + /// + /// Called when the component state changes. + /// + public event StateChanged OnChange; + #endregion + + /// + /// Initializes this StateSync with a component. + /// + /// + public void Initialize(T component) + { + Component = component; + } + + /// + /// Sets the enabled state for Component. + /// + /// + private void SetState(bool enabled) + { + if (base.NetworkManager == null) + return; + + if (Component == null) + NetworkManager.LogError($"State cannot be changed as Initialize has not been called with a valid component."); + + //If hasn't changed then ignore. + bool prev = GetState(); + if (enabled == prev) + return; + + //Set to new value and add operation. + Component.enabled = enabled; + AddOperation(Component, prev, enabled); + } + + /// + /// Gets the enabled state for Component. + /// + /// + private bool GetState() + { + return Component.enabled; + } + + /// + /// Adds an operation to synchronize. + /// + private void AddOperation(T component, bool prev, bool next) + { + if (!base.IsRegistered) + return; + + if (base.NetworkManager != null && !base.NetworkBehaviour.IsServer) + { + NetworkManager.LogWarning($"Cannot complete operation as server when server is not active."); + return; + } + + base.Dirty(); + + //Data can currently only be set from server, so this is always asServer. + bool asServer = true; + OnChange?.Invoke(component, prev, next, asServer); + } + /// + /// Writes all changed values. + /// + ///True to set the next time data may sync. + public override void WriteDelta(PooledWriter writer, bool resetSyncTick = true) + { + base.WriteDelta(writer, resetSyncTick); + writer.WriteBoolean(Component.enabled); + } + + /// + /// Writes all values. + /// + public override void WriteFull(PooledWriter writer) + { + /* Always write full for this custom sync type. + * It would be difficult to know if the + * state has changed given it's a boolean, and + * may or may not be true/false after pooling is added. */ + WriteDelta(writer, false); + } + + /// + /// Reads and sets the current values for server or client. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [APIExclude] + public override void Read(PooledReader reader, bool asServer) + { + bool nextValue = reader.ReadBoolean(); + if (base.NetworkManager == null) + return; + + bool prevValue = GetState(); + + /* When !asServer don't make changes if server is running. + * This is because changes would have already been made on + * the server side and doing so again would result in duplicates + * and potentially overwrite data not yet sent. */ + bool asClientAndHost = (!asServer && base.NetworkManager.IsServer); + if (!asClientAndHost) + Component.enabled = nextValue; + + OnChange?.Invoke(Component, prevValue, nextValue, asServer); + } + + /// + /// Return the serialized type. + /// + /// + public object GetSerializedType() => typeof(bool); + } +} diff --git a/Assets/FishNet/Example/All/CustomSyncType/Component State Sync/ComponentStateSync.cs.meta b/Assets/FishNet/Example/All/CustomSyncType/Component State Sync/ComponentStateSync.cs.meta new file mode 100644 index 0000000..7ba490b --- /dev/null +++ b/Assets/FishNet/Example/All/CustomSyncType/Component State Sync/ComponentStateSync.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ce7cdae4a8f3d914fa9141b4bd760faa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/CustomSyncType/Component State Sync/ComponentSyncStateBehaviour.cs b/Assets/FishNet/Example/All/CustomSyncType/Component State Sync/ComponentSyncStateBehaviour.cs new file mode 100644 index 0000000..f9adbce --- /dev/null +++ b/Assets/FishNet/Example/All/CustomSyncType/Component State Sync/ComponentSyncStateBehaviour.cs @@ -0,0 +1,43 @@ +using FishNet.Object; +using FishNet.Object.Synchronizing; +using UnityEngine; + +namespace FishNet.Example.ComponentStateSync +{ + + public class ComponentSyncStateBehaviour : NetworkBehaviour + { + /// + /// Using my custom SyncType for Structy. + /// + [SyncObject] + private readonly ComponentStateSync _syncScript = new ComponentStateSync(); + + private void Awake() + { + AMonoScript ams = GetComponent(); + //Initialize with the component of your choice. + _syncScript.Initialize(ams); + //Optionally listen for changes. + _syncScript.OnChange += _syncScript_OnChange; + } + + /// + /// Called when enabled state changes for SyncScript. + /// + private void _syncScript_OnChange(AMonoScript component, bool prevState, bool nextState, bool asServer) + { + Debug.Log($"Change received on {component.GetType().Name}. New value is {nextState}. Received asServer {asServer}."); + } + + private void Update() + { + //Every so often flip the state of the component. + if (base.IsServer && Time.frameCount % 200 == 0) + _syncScript.Enabled = !_syncScript.Enabled; + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Example/All/CustomSyncType/Component State Sync/ComponentSyncStateBehaviour.cs.meta b/Assets/FishNet/Example/All/CustomSyncType/Component State Sync/ComponentSyncStateBehaviour.cs.meta new file mode 100644 index 0000000..eba67fc --- /dev/null +++ b/Assets/FishNet/Example/All/CustomSyncType/Component State Sync/ComponentSyncStateBehaviour.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a7327b94eff9c7d4aa876aa54ca6b439 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/CustomSyncType/Custom Struct Sync.meta b/Assets/FishNet/Example/All/CustomSyncType/Custom Struct Sync.meta new file mode 100644 index 0000000..5cc762d --- /dev/null +++ b/Assets/FishNet/Example/All/CustomSyncType/Custom Struct Sync.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 449825c1f60f1e443b081834b84df95d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/CustomSyncType/Custom Struct Sync/StructSyncBehaviour.cs b/Assets/FishNet/Example/All/CustomSyncType/Custom Struct Sync/StructSyncBehaviour.cs new file mode 100644 index 0000000..1746ad2 --- /dev/null +++ b/Assets/FishNet/Example/All/CustomSyncType/Custom Struct Sync/StructSyncBehaviour.cs @@ -0,0 +1,41 @@ +using FishNet.Object; +using FishNet.Object.Synchronizing; +using UnityEngine; + +namespace FishNet.Example.CustomSyncObject +{ + + public class StructSyncBehaviour : NetworkBehaviour + { + /// + /// Using my custom SyncType for Structy. + /// + [SyncObject] + private readonly StructySync _structy = new StructySync(); + + private void Awake() + { + //Listen for change events. + _structy.OnChange += _structy_OnChange; + } + + private void _structy_OnChange(StructySync.CustomOperation op, Structy oldItem, Structy newItem, bool asServer) + { + Debug.Log("Changed " + op.ToString() + ", " + newItem.Age + ", " + asServer); + } + + private void Update() + { + //Every so often increase the age property on structy using StructySync, my custom sync type. + if (base.IsServer && Time.frameCount % 200 == 0) + { + //Increase the age and set that values have changed. + _structy.Value.Age += 1; + _structy.ValuesChanged(); + } + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Example/All/CustomSyncType/Custom Struct Sync/StructSyncBehaviour.cs.meta b/Assets/FishNet/Example/All/CustomSyncType/Custom Struct Sync/StructSyncBehaviour.cs.meta new file mode 100644 index 0000000..fc5e85e --- /dev/null +++ b/Assets/FishNet/Example/All/CustomSyncType/Custom Struct Sync/StructSyncBehaviour.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c2cc7cbbeb4170642ac60367303222ef +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/CustomSyncType/Custom Struct Sync/StructySync.cs b/Assets/FishNet/Example/All/CustomSyncType/Custom Struct Sync/StructySync.cs new file mode 100644 index 0000000..2787bb1 --- /dev/null +++ b/Assets/FishNet/Example/All/CustomSyncType/Custom Struct Sync/StructySync.cs @@ -0,0 +1,256 @@ +using FishNet.Documenting; +using FishNet.Object.Synchronizing; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace FishNet.Example.CustomSyncObject +{ + /// + /// This is the data type I want to create a custom SyncType for. + /// + public struct Structy + { + public string Name; + public ushort Age; + + public Structy(string name, ushort age) + { + Name = name; + Age = age; + } + } + + /// + /// It's very important to exclude this from codegen. + /// However, whichever value you are synchronizing must not be excluded. This is why the value is outside the StructySync class. + /// + public class StructySync : SyncBase, ICustomSync + { + #region Types. + /// + /// Information about how the struct has changed. + /// You could send the entire struct on every change + /// but this is an example of how you might send individual changed + /// fields. + /// + private struct ChangeData + { + internal CustomOperation Operation; + internal Structy Data; + + public ChangeData(CustomOperation operation, Structy data) + { + Operation = operation; + Data = data; + } + } + /// + /// Types of changes. This is related to ChangedData + /// where you can specify what has changed. + /// + public enum CustomOperation : byte + { + Full = 0, + Name = 1, + Age = 2 + } + #endregion + + #region Public. + /// + /// Delegate signature for when Structy changes. + /// + /// + /// + /// + public delegate void CustomChanged(CustomOperation op, Structy oldItem, Structy newItem, bool asServer); + /// + /// Called when the Structy changes. + /// + public event CustomChanged OnChange; + /// + /// Current value of Structy. + /// + public Structy Value = new Structy(); + #endregion + + #region Private. + /// + /// Initial value when initialized. Used to reset this sync type. + /// + private Structy _initialValue; + /// + /// Changed data which will be sent next tick. + /// + private readonly List _changed = new List(); + /// + /// True if values have changed since initialization. + /// + private bool _valuesChanged; + /// + /// Last value after dirty call. + /// + private Structy _lastDirtied = new Structy(); + #endregion + + protected override void Registered() + { + base.Registered(); + _initialValue = Value; + } + + /// + /// Adds an operation and invokes locally. + /// + /// + /// + /// + /// + private void AddOperation(CustomOperation operation, Structy prev, Structy next) + { + if (!base.IsRegistered) + return; + + if (base.NetworkManager != null && !base.NetworkBehaviour.IsServer) + { + NetworkManager.LogWarning($"Cannot complete operation as server when server is not active."); + return; + } + + /* Set as changed even if cannot dirty. + * Dirty is only set when there are observers, + * but even if there are not observers + * values must be marked as changed so when + * there are observers, new values are sent. */ + _valuesChanged = true; + base.Dirty(); + + //Data can currently only be set from server, so this is always asServer. + bool asServer = true; + //Add to changed. + ChangeData cd = new ChangeData(operation, next); + _changed.Add(cd); + OnChange?.Invoke(operation, prev, next, asServer); + } + + /// + /// Writes all changed values. + /// + /// + ///True to set the next time data may sync. + public override void WriteDelta(PooledWriter writer, bool resetSyncTick = true) + { + base.WriteDelta(writer, resetSyncTick); + writer.WriteInt32(_changed.Count); + + for (int i = 0; i < _changed.Count; i++) + { + ChangeData change = _changed[i]; + writer.WriteByte((byte)change.Operation); + + //Clear does not need to write anymore data so it is not included in checks. + if (change.Operation == CustomOperation.Age) + { + writer.WriteUInt16(change.Data.Age); + } + else if (change.Operation == CustomOperation.Name) + { + writer.WriteString(change.Data.Name); + } + } + + _changed.Clear(); + } + + /// + /// Writes all values if not initial values. + /// + /// + public override void WriteFull(PooledWriter writer) + { + if (!_valuesChanged) + return; + + base.WriteHeader(writer, false); + //Write one change. + writer.WriteInt32(1); + //Write if changed is from the server, so always use the server _value. + writer.WriteByte((byte)CustomOperation.Full); + //Write value. + writer.Write(Value); + } + + /// + /// Reads and sets the current values for server or client. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [APIExclude] + public override void Read(PooledReader reader, bool asServer) + { + /* When !asServer don't make changes if server is running. + * This is because changes would have already been made on + * the server side and doing so again would result in duplicates + * and potentially overwrite data not yet sent. */ + bool asClientAndHost = (!asServer && base.NetworkManager.IsServer); + + int changes = reader.ReadInt32(); + for (int i = 0; i < changes; i++) + { + CustomOperation operation = (CustomOperation)reader.ReadByte(); + Structy prev = Value; + Structy next = prev; + + //Full. + if (operation == CustomOperation.Full) + next = reader.Read(); + //Name. + else if (operation == CustomOperation.Name) + next.Name = reader.ReadString(); + //Age + else if (operation == CustomOperation.Age) + next.Age = reader.ReadUInt16(); + + OnChange?.Invoke(operation, prev, next, asServer); + + if (!asClientAndHost) + Value = next; + } + + } + + /// + /// Checks Value for changes and sends them to clients. + /// + public void ValuesChanged() + { + Structy prev = _lastDirtied; + Structy current = Value; + + if (prev.Name != current.Name) + AddOperation(CustomOperation.Name, prev, current); + if (prev.Age != current.Age) + AddOperation(CustomOperation.Age, prev, current); + + _lastDirtied = Value; + } + + /// + /// Resets to initialized values. + /// + public override void Reset() + { + base.Reset(); + _changed.Clear(); + Value = _initialValue; + _valuesChanged = false; + } + + /// + /// Return the serialized type. + /// + /// + public object GetSerializedType() => typeof(Structy); + } +} diff --git a/Assets/FishNet/Example/All/CustomSyncType/Custom Struct Sync/StructySync.cs.meta b/Assets/FishNet/Example/All/CustomSyncType/Custom Struct Sync/StructySync.cs.meta new file mode 100644 index 0000000..68e9a15 --- /dev/null +++ b/Assets/FishNet/Example/All/CustomSyncType/Custom Struct Sync/StructySync.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4c0a84e32efc60f4aa471e3a42cf2e0b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction.meta b/Assets/FishNet/Example/All/Prediction.meta new file mode 100644 index 0000000..9306c8c --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 311aaa607bcf0c142a09726e4f6038ad +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/CharacterController.meta b/Assets/FishNet/Example/All/Prediction/CharacterController.meta new file mode 100644 index 0000000..ad3e5b6 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/CharacterController.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 79558f739187b5848bff30f39a16aaa7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/CharacterController/CharacterControllerPrediction.unity b/Assets/FishNet/Example/All/Prediction/CharacterController/CharacterControllerPrediction.unity new file mode 100644 index 0000000..413e4d3 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/CharacterController/CharacterControllerPrediction.unity @@ -0,0 +1,1276 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!114 &192429404 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408886491487334} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3fdaae44044276a49a52229c1597e33b, type: 3} + m_Name: + m_EditorClassIdentifier: + _updateOrder: 0 + _timingType: 0 + _allowTickDropping: 0 + _maximumFrameTicks: 2 + _tickRate: 15 + _pingInterval: 1 + _timingInterval: 2 + _physicsMode: 1 +--- !u!1 &555580081 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 555580085} + - component: {fileID: 555580084} + - component: {fileID: 555580083} + - component: {fileID: 555580082} + m_Layer: 0 + m_Name: Sphere + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!135 &555580082 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 555580081} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &555580083 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 555580081} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 49a3799e31595ea478b5dd6fa163fcbd, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &555580084 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 555580081} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &555580085 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 555580081} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -9.16, y: -3.4, z: 13.1} + m_LocalScale: {x: 15, y: 15, z: 15} + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &872683029 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 872683031} + - component: {fileID: 872683030} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &872683030 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 872683029} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &872683031 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 872683029} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &1112005912 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1112005916} + - component: {fileID: 1112005915} + - component: {fileID: 1112005914} + - component: {fileID: 1112005913} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &1112005913 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1112005912} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1112005914 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1112005912} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 0bb31cf72dfcef449a1a4a5aab857f63, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &1112005915 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1112005912} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1112005916 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1112005912} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: -2, z: 0} + m_LocalScale: {x: 100, y: 1, z: 100} + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1470934487 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1470934491} + - component: {fileID: 1470934490} + - component: {fileID: 1470934489} + - component: {fileID: 1470934488} + m_Layer: 0 + m_Name: Cube (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &1470934488 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1470934487} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1470934489 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1470934487} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 49a3799e31595ea478b5dd6fa163fcbd, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &1470934490 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1470934487} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1470934491 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1470934487} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 12.11, y: -0.08, z: 0} + m_LocalScale: {x: 1, y: 5, z: 20} + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1784594014 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1784594015} + m_Layer: 0 + m_Name: Level + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1784594015 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1784594014} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1112005916} + - {fileID: 1470934491} + - {fileID: 1852016427} + - {fileID: 872683031} + - {fileID: 555580085} + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1852016424 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1852016427} + - component: {fileID: 1852016426} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!20 &1852016426 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1852016424} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &1852016427 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1852016424} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1424052073902602981 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2480283714093027852} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.25490198, g: 0.25490198, b: 0.25490198, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 2b3dca501a9d8c8479dc71dd068aa8b8, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!1 &2480283714093027852 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 9139860295689780510} + - component: {fileID: 6745855428745291286} + - component: {fileID: 1424052073902602981} + m_Layer: 5 + m_Name: Indicator + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &3965864432699664111 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4808982256744730386} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.25490198, g: 0.25490198, b: 0.25490198, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 2b3dca501a9d8c8479dc71dd068aa8b8, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &4393252310837120370 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252310837120383} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 2d50394614f8feb4eb0567fb7618d84d, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &4393252310837120371 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252310837120383} + m_CullTransparentMesh: 0 +--- !u!224 &4393252310837120380 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252310837120383} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 9139860295689780510} + m_Father: {fileID: 4393252311378272345} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 16, y: -96} + m_SizeDelta: {x: 256, y: 64} + m_Pivot: {x: 0, y: 1} +--- !u!114 &4393252310837120381 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252310837120383} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 4393252310837120370} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 4393252311378272325} + m_MethodName: OnClick_Client + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!1 &4393252310837120383 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4393252310837120380} + - component: {fileID: 4393252310837120371} + - component: {fileID: 4393252310837120370} + - component: {fileID: 4393252310837120381} + m_Layer: 5 + m_Name: Client + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &4393252311222810866 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311222810879} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 1b187e63031bf7849b249c8212440c3b, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &4393252311222810867 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311222810879} + m_CullTransparentMesh: 0 +--- !u!224 &4393252311222810876 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311222810879} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 7233259200132978428} + m_Father: {fileID: 4393252311378272345} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 16, y: -16} + m_SizeDelta: {x: 256, y: 64} + m_Pivot: {x: 0, y: 1} +--- !u!114 &4393252311222810877 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311222810879} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 4393252311222810866} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 4393252311378272325} + m_MethodName: OnClick_Server + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!1 &4393252311222810879 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4393252311222810876} + - component: {fileID: 4393252311222810867} + - component: {fileID: 4393252311222810866} + - component: {fileID: 4393252311222810877} + m_Layer: 5 + m_Name: Server + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &4393252311378272324 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4393252311378272345} + - component: {fileID: 4393252311378272325} + - component: {fileID: 4393252311378272344} + - component: {fileID: 4393252311378272347} + - component: {fileID: 4393252311378272346} + m_Layer: 5 + m_Name: NetworkHudCanvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &4393252311378272325 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311378272324} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6d3606bfdac5a4743890fc1a5ecd8f24, type: 3} + m_Name: + m_EditorClassIdentifier: + _autoStartType: 1 + _stoppedColor: {r: 0.25490198, g: 0.25490198, b: 0.25490198, a: 1} + _changingColor: {r: 0.78431374, g: 0.6862745, b: 0, a: 1} + _startedColor: {r: 0, g: 0.5882353, b: 0.64705884, a: 1} + _serverIndicator: {fileID: 3965864432699664111} + _clientIndicator: {fileID: 1424052073902602981} +--- !u!223 &4393252311378272344 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311378272324} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &4393252311378272345 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311378272324} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_Children: + - {fileID: 4393252311222810876} + - {fileID: 4393252310837120380} + m_Father: {fileID: 7443408886491487332} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!114 &4393252311378272346 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311378272324} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &4393252311378272347 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311378272324} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 1 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 1920, y: 1080} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0.5 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 +--- !u!1 &4808982256744730386 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7233259200132978428} + - component: {fileID: 5104387649508117141} + - component: {fileID: 3965864432699664111} + m_Layer: 5 + m_Name: Indicator + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!222 &5104387649508117141 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4808982256744730386} + m_CullTransparentMesh: 0 +--- !u!222 &6745855428745291286 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2480283714093027852} + m_CullTransparentMesh: 0 +--- !u!224 &7233259200132978428 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4808982256744730386} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -1} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4393252311222810876} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!4 &7443408886491487332 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408886491487334} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4393252311378272345} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &7443408886491487334 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7443408886491487332} + - component: {fileID: 7443408886491487335} + - component: {fileID: 7443408886491487337} + - component: {fileID: 192429404} + - component: {fileID: 7443408886491487336} + - component: {fileID: 7443408886491487339} + - component: {fileID: 7443408886491487338} + m_Layer: 0 + m_Name: NetworkManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &7443408886491487335 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408886491487334} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d2c95dfde7d73b54dbbdc23155d35d36, type: 3} + m_Name: + m_EditorClassIdentifier: + _logging: {fileID: 0} + _spawnablePrefabs: {fileID: 11400000, guid: 3a54436bdb916194f99da0d17231e617, type: 2} + _refreshDefaultPrefabs: 0 + _runInBackground: 1 + _dontDestroyOnLoad: 1 + _objectPool: {fileID: 0} + _persistence: 0 +--- !u!114 &7443408886491487336 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408886491487334} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f9b6b565cd9533c4ebc18003f0fc18a2, type: 3} + m_Name: + m_EditorClassIdentifier: + _color: {r: 1, g: 1, b: 1, a: 1} + _placement: 1 + _hideTickRate: 1 +--- !u!114 &7443408886491487337 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408886491487334} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 211a9f6ec51ddc14f908f5acc0cd0423, type: 3} + m_Name: + m_EditorClassIdentifier: + _playerPrefab: {fileID: 9117857247562382210, guid: b2991431a5f893e49937d01b6da44ff8, + type: 3} + _addToDefaultScene: 1 + Spawns: [] +--- !u!114 &7443408886491487338 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408886491487334} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6f48f002b825cbd45a19bd96d90f9edb, type: 3} + m_Name: + m_EditorClassIdentifier: + _unreliableMTU: 1023 + _ipv4BindAddress: + _ipv6BindAddress: + _port: 7771 + _maximumClients: 9999 + _clientAddress: localhost + _timeout: 15 +--- !u!114 &7443408886491487339 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408886491487334} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 34e4a322dca349547989b14021da4e23, type: 3} + m_Name: + m_EditorClassIdentifier: + Transport: {fileID: 7443408886491487338} + _intermediateLayer: {fileID: 0} + _latencySimulator: + _enabled: 0 + _simulateHost: 1 + _latency: 0 + _outOfOrder: 0 + _packetLoss: 0 +--- !u!224 &9139860295689780510 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2480283714093027852} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -1} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4393252310837120380} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} diff --git a/Assets/FishNet/Example/All/Prediction/CharacterController/CharacterControllerPrediction.unity.meta b/Assets/FishNet/Example/All/Prediction/CharacterController/CharacterControllerPrediction.unity.meta new file mode 100644 index 0000000..0530583 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/CharacterController/CharacterControllerPrediction.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: ec1a6e85f57626a4cbacaf306766bdfd +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/CharacterController/Prefabs.meta b/Assets/FishNet/Example/All/Prediction/CharacterController/Prefabs.meta new file mode 100644 index 0000000..1be0a57 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/CharacterController/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 26b1206536a5ae44792afd5292ad29b7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/CharacterController/Prefabs/CharacterControllerPrediction.prefab b/Assets/FishNet/Example/All/Prediction/CharacterController/Prefabs/CharacterControllerPrediction.prefab new file mode 100644 index 0000000..65e8301 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/CharacterController/Prefabs/CharacterControllerPrediction.prefab @@ -0,0 +1,272 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &50058147544064912 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8274935638305073705} + - component: {fileID: 5937955550093657123} + - component: {fileID: 8512892203317417748} + m_Layer: 0 + m_Name: Capsule + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &8274935638305073705 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 50058147544064912} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 9117857247562382215} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &5937955550093657123 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 50058147544064912} + m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &8512892203317417748 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 50058147544064912} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!1 &9117857247562382221 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 9117857247562382215} + - component: {fileID: 9117857247562382208} + - component: {fileID: 9117857247562382210} + - component: {fileID: 9144896207351220584} + - component: {fileID: 9144896207351220583} + - component: {fileID: 4742920392281400910} + - component: {fileID: -947403508163804576} + m_Layer: 0 + m_Name: CharacterControllerPrediction + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &9117857247562382215 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9117857247562382221} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 8274935638305073705} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!136 &9117857247562382208 +CapsuleCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9117857247562382221} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + m_Radius: 0.5 + m_Height: 2 + m_Direction: 1 + m_Center: {x: 0, y: 0, z: 0} +--- !u!114 &9117857247562382210 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9117857247562382221} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + NetworkObserver: {fileID: 0} + k__BackingField: 0 + k__BackingField: 0 + _scenePathHash: 0 + k__BackingField: 0 + k__BackingField: 14762131302109388384 + _sceneNetworkObjects: [] + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: {fileID: 0} + _networkBehaviours: + - {fileID: 9144896207351220584} + - {fileID: 4742920392281400910} + - {fileID: -947403508163804576} + k__BackingField: {fileID: 0} + k__BackingField: [] + _isNetworked: 1 + _isGlobal: 0 + _initializeOrder: 0 + _defaultDespawnType: 0 +--- !u!114 &9144896207351220584 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9117857247562382221} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6bb7bd88fd11b5d4aa7fca3d42172ab8, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 9117857247562382210} + _networkObjectCache: {fileID: 9117857247562382210} + _moveRate: 5 +--- !u!143 &9144896207351220583 +CharacterController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9117857247562382221} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Height: 2 + m_Radius: 0.5 + m_SlopeLimit: 45 + m_StepOffset: 0.3 + m_SkinWidth: 0.08 + m_MinMoveDistance: 0.001 + m_Center: {x: 0, y: 0, z: 0} +--- !u!114 &4742920392281400910 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9117857247562382221} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 402926ef33e0a894d9fec352693988ac, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 1 + _addedNetworkObject: {fileID: 0} + _networkObjectCache: {fileID: 9117857247562382210} + _implementsPredictionMethods: 1 + _graphicalObject: {fileID: 8274935638305073705} + _enableTeleport: 0 + _teleportThreshold: 1 + _ownerSmoothPosition: 1 + _ownerSmoothRotation: 1 + _ownerInterpolation: 1 + _predictionType: 0 + _rigidbody: {fileID: 0} + _rigidbody2d: {fileID: 0} + _spectatorSmoothPosition: 1 + _spectatorSmoothRotation: 1 + _spectatorInterpolation: 4 + _overflowMultiplier: 0.1 + _maintainedVelocity: 0 + _resendType: 0 + _resendInterval: 30 + _networkTransform: {fileID: -947403508163804576} +--- !u!114 &-947403508163804576 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9117857247562382221} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a2836e36774ca1c4bbbee976e17b649c, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 2 + _addedNetworkObject: {fileID: 9117857247562382210} + _networkObjectCache: {fileID: 9117857247562382210} + _synchronizeParent: 0 + _packing: + Position: 1 + Rotation: 1 + Scale: 0 + _interpolation: 2 + _extrapolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + _clientAuthoritative: 1 + _sendToOwner: 1 + _synchronizePosition: 1 + _positionSnapping: + X: 0 + Y: 0 + Z: 0 + _synchronizeRotation: 1 + _rotationSnapping: + X: 0 + Y: 0 + Z: 0 + _synchronizeScale: 1 + _scaleSnapping: + X: 0 + Y: 0 + Z: 0 diff --git a/Assets/FishNet/Example/All/Prediction/CharacterController/Prefabs/CharacterControllerPrediction.prefab.meta b/Assets/FishNet/Example/All/Prediction/CharacterController/Prefabs/CharacterControllerPrediction.prefab.meta new file mode 100644 index 0000000..2ffd376 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/CharacterController/Prefabs/CharacterControllerPrediction.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b2991431a5f893e49937d01b6da44ff8 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/CharacterController/Scripts.meta b/Assets/FishNet/Example/All/Prediction/CharacterController/Scripts.meta new file mode 100644 index 0000000..e18185d --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/CharacterController/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 17c942141382c1c45abd049b9f01c633 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/CharacterController/Scripts/CharacterControllerPrediction.cs b/Assets/FishNet/Example/All/Prediction/CharacterController/Scripts/CharacterControllerPrediction.cs new file mode 100644 index 0000000..9b4c797 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/CharacterController/Scripts/CharacterControllerPrediction.cs @@ -0,0 +1,127 @@ +using FishNet; +using FishNet.Object; +using FishNet.Object.Prediction; +using FishNet.Transporting; +using UnityEngine; + +/* +* +* See TransformPrediction.cs for more detailed notes. +* +*/ + +namespace FishNet.Example.Prediction.CharacterControllers +{ + + public class CharacterControllerPrediction : NetworkBehaviour + { + #region Types. + public struct MoveData : IReplicateData + { + public float Horizontal; + public float Vertical; + + private uint _tick; + public void Dispose() { } + public uint GetTick() => _tick; + public void SetTick(uint value) => _tick = value; + } + public struct ReconcileData : IReconcileData + { + public Vector3 Position; + public Quaternion Rotation; + public ReconcileData(Vector3 position, Quaternion rotation) + { + Position = position; + Rotation = rotation; + _tick = 0; + } + + private uint _tick; + public void Dispose() { } + public uint GetTick() => _tick; + public void SetTick(uint value) => _tick = value; + } + #endregion + + #region Serialized. + [SerializeField] + private float _moveRate = 5f; + #endregion + + #region Private. + private CharacterController _characterController; + #endregion + + private void Awake() + { + InstanceFinder.TimeManager.OnTick += TimeManager_OnTick; + _characterController = GetComponent(); + } + + public override void OnStartClient() + { + base.OnStartClient(); + _characterController.enabled = (base.IsServer || base.IsOwner); + } + + private void OnDestroy() + { + if (InstanceFinder.TimeManager != null) + { + InstanceFinder.TimeManager.OnTick -= TimeManager_OnTick; + } + } + + private void TimeManager_OnTick() + { + if (base.IsOwner) + { + Reconciliation(default, false); + CheckInput(out MoveData md); + Move(md, false); + } + if (base.IsServer) + { + Move(default, true); + ReconcileData rd = new ReconcileData(transform.position, transform.rotation); + Reconciliation(rd, true); + } + } + + private void CheckInput(out MoveData md) + { + md = default; + + float horizontal = Input.GetAxisRaw("Horizontal"); + float vertical = Input.GetAxisRaw("Vertical"); + + if (horizontal == 0f && vertical == 0f) + return; + + md = new MoveData() + { + Horizontal = horizontal, + Vertical = vertical + }; + } + + [Replicate] + private void Move(MoveData md, bool asServer, Channel channel = Channel.Unreliable, bool replaying = false) + { + Vector3 move = new Vector3(md.Horizontal, 0f, md.Vertical).normalized + new Vector3(0f, Physics.gravity.y, 0f); + _characterController.Move(move * _moveRate * (float)base.TimeManager.TickDelta); + } + + [Reconcile] + private void Reconciliation(ReconcileData rd, bool asServer, Channel channel = Channel.Unreliable) + { + transform.position = rd.Position; + transform.rotation = rd.Rotation; + } + + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Example/All/Prediction/CharacterController/Scripts/CharacterControllerPrediction.cs.meta b/Assets/FishNet/Example/All/Prediction/CharacterController/Scripts/CharacterControllerPrediction.cs.meta new file mode 100644 index 0000000..04a2558 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/CharacterController/Scripts/CharacterControllerPrediction.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6bb7bd88fd11b5d4aa7fca3d42172ab8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/Materials.meta b/Assets/FishNet/Example/All/Prediction/Materials.meta new file mode 100644 index 0000000..e174353 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Materials.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e3efb1eab37402045a64161aba21ed29 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/Materials/BlueMat.mat b/Assets/FishNet/Example/All/Prediction/Materials/BlueMat.mat new file mode 100644 index 0000000..df63253 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Materials/BlueMat.mat @@ -0,0 +1,77 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: BlueMat + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 0 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 1 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0.15970986, g: 0.50941056, b: 0.9150943, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/Assets/FishNet/Example/All/Prediction/Materials/BlueMat.mat.meta b/Assets/FishNet/Example/All/Prediction/Materials/BlueMat.mat.meta new file mode 100644 index 0000000..fde4d4f --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Materials/BlueMat.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ea31fef5ca1bc7344a72c71a5f9a0cd2 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/Materials/GroundMat.mat b/Assets/FishNet/Example/All/Prediction/Materials/GroundMat.mat new file mode 100644 index 0000000..8c769b6 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Materials/GroundMat.mat @@ -0,0 +1,77 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: GroundMat + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 0 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 1 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0.3679245, g: 0.36271805, b: 0.36271805, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/Assets/FishNet/Example/All/Prediction/Materials/GroundMat.mat.meta b/Assets/FishNet/Example/All/Prediction/Materials/GroundMat.mat.meta new file mode 100644 index 0000000..3a74ead --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Materials/GroundMat.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0bb31cf72dfcef449a1a4a5aab857f63 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/Materials/PurpleMat.mat b/Assets/FishNet/Example/All/Prediction/Materials/PurpleMat.mat new file mode 100644 index 0000000..e9f384c --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Materials/PurpleMat.mat @@ -0,0 +1,77 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: PurpleMat + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 0 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 1 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0.3364354, g: 0.0990566, b: 0.3962264, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/Assets/FishNet/Example/All/Prediction/Materials/PurpleMat.mat.meta b/Assets/FishNet/Example/All/Prediction/Materials/PurpleMat.mat.meta new file mode 100644 index 0000000..8c782d4 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Materials/PurpleMat.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 49a3799e31595ea478b5dd6fa163fcbd +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/Materials/YellowishMat.mat b/Assets/FishNet/Example/All/Prediction/Materials/YellowishMat.mat new file mode 100644 index 0000000..0f8028c --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Materials/YellowishMat.mat @@ -0,0 +1,77 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: YellowishMat + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 0 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 1 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0.62352943, g: 0.5504006, b: 0.17254901, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/Assets/FishNet/Example/All/Prediction/Materials/YellowishMat.mat.meta b/Assets/FishNet/Example/All/Prediction/Materials/YellowishMat.mat.meta new file mode 100644 index 0000000..9f32cc6 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Materials/YellowishMat.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6273444b68d517449aadb36abebaf561 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/Rigidbody.meta b/Assets/FishNet/Example/All/Prediction/Rigidbody.meta new file mode 100644 index 0000000..06da42f --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Rigidbody.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d851e0508fe14f44dae11c460021a6c2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/Rigidbody/New Physic Material.physicMaterial b/Assets/FishNet/Example/All/Prediction/Rigidbody/New Physic Material.physicMaterial new file mode 100644 index 0000000..32e5794 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Rigidbody/New Physic Material.physicMaterial @@ -0,0 +1,14 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!134 &13400000 +PhysicMaterial: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: New Physic Material + dynamicFriction: 0.1 + staticFriction: 0.1 + bounciness: 0.9 + frictionCombine: 1 + bounceCombine: 0 diff --git a/Assets/FishNet/Example/All/Prediction/Rigidbody/New Physic Material.physicMaterial.meta b/Assets/FishNet/Example/All/Prediction/Rigidbody/New Physic Material.physicMaterial.meta new file mode 100644 index 0000000..c98b07f --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Rigidbody/New Physic Material.physicMaterial.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2315b89c915a21c40982b5867ff7d343 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 13400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/Rigidbody/Prefabs.meta b/Assets/FishNet/Example/All/Prediction/Rigidbody/Prefabs.meta new file mode 100644 index 0000000..c67282a --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Rigidbody/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 185d003306f80ff4cb148c3f06bf3318 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/Rigidbody/Prefabs/PredictedBullet.prefab b/Assets/FishNet/Example/All/Prediction/Rigidbody/Prefabs/PredictedBullet.prefab new file mode 100644 index 0000000..582bdcd --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Rigidbody/Prefabs/PredictedBullet.prefab @@ -0,0 +1,256 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &3624261834906416581 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3624261834906416580} + - component: {fileID: 3624261834906416603} + - component: {fileID: 3624261834906416600} + - component: {fileID: 3624261834906416601} + - component: {fileID: 3624261834906416602} + - component: {fileID: -6246116784124330627} + - component: {fileID: 7850526870340082268} + m_Layer: 0 + m_Name: PredictedBullet + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3624261834906416580 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3624261834906416581} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 15.312408, y: 6.390984, z: 25.2406} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 3624261835462115340} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!135 &3624261834906416603 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3624261834906416581} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!114 &3624261834906416600 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3624261834906416581} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 402926ef33e0a894d9fec352693988ac, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 3624261834906416601} + _networkObjectCache: {fileID: 3624261834906416601} + _implementsPredictionMethods: 0 + _graphicalObject: {fileID: 3624261835462115340} + _enableTeleport: 0 + _teleportThreshold: 200 + _ownerSmoothPosition: 1 + _ownerSmoothRotation: 1 + _ownerInterpolation: 1 + _predictionType: 1 + _rigidbody: {fileID: 3624261834906416602} + _rigidbody2d: {fileID: 0} + _spectatorSmoothPosition: 1 + _spectatorSmoothRotation: 1 + _spectatorSmoothingType: 2 + _customSmoothingData: + InterpolationPercent: 1 + CollisionInterpolationPercent: 0.1 + InterpolationDecreaseStep: 1 + InterpolationIncreaseStep: 3 + _preconfiguredSmoothingDataPreview: + InterpolationPercent: 1.5 + CollisionInterpolationPercent: 0.2 + InterpolationDecreaseStep: 1 + InterpolationIncreaseStep: 5 + _maintainedVelocity: 0 + _resendType: 0 + _resendInterval: 30 + _networkTransform: {fileID: 0} +--- !u!114 &3624261834906416601 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3624261834906416581} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + NetworkObserver: {fileID: 0} + k__BackingField: 5 + k__BackingField: 0 + _scenePathHash: 2281302723 + k__BackingField: 0 + k__BackingField: 12629563467294206692 + _sceneNetworkObjects: + - {fileID: 3624261834906416601} + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: {fileID: 7850526870340082268} + _networkBehaviours: + - {fileID: 3624261834906416600} + - {fileID: -6246116784124330627} + - {fileID: 7850526870340082268} + k__BackingField: {fileID: 0} + k__BackingField: [] + _isNetworked: 1 + _isGlobal: 0 + _initializeOrder: 0 + _defaultDespawnType: 0 +--- !u!54 &3624261834906416602 +Rigidbody: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3624261834906416581} + serializedVersion: 2 + m_Mass: 1 + m_Drag: 0 + m_AngularDrag: 0.05 + m_UseGravity: 0 + m_IsKinematic: 0 + m_Interpolate: 0 + m_Constraints: 0 + m_CollisionDetection: 3 +--- !u!114 &-6246116784124330627 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3624261834906416581} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fc7792d9dc338aa4dbc8a2f0533cf6ec, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 1 + _addedNetworkObject: {fileID: 3624261834906416601} + _networkObjectCache: {fileID: 3624261834906416601} + _startingForce: {x: 0, y: 0, z: 0} +--- !u!114 &7850526870340082268 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3624261834906416581} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e2b597e1828355a4d994a69cbb11ef85, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 2 + _addedNetworkObject: {fileID: 3624261834906416601} + _networkObjectCache: {fileID: 3624261834906416601} + _allowSpawning: 1 + _allowDespawning: 1 + _allowSyncTypes: 1 +--- !u!1 &3624261835462115341 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3624261835462115340} + - component: {fileID: 3624261835462115330} + - component: {fileID: 3624261835462115331} + m_Layer: 0 + m_Name: Graphics + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3624261835462115340 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3624261835462115341} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 3624261834906416580} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &3624261835462115330 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3624261835462115341} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &3624261835462115331 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3624261835462115341} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 diff --git a/Assets/FishNet/Example/All/Prediction/Rigidbody/Prefabs/PredictedBullet.prefab.meta b/Assets/FishNet/Example/All/Prediction/Rigidbody/Prefabs/PredictedBullet.prefab.meta new file mode 100644 index 0000000..c63f512 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Rigidbody/Prefabs/PredictedBullet.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e3f599c0ac723194a92f51c91ec73267 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/Rigidbody/Prefabs/PredictedSphereSorta.prefab b/Assets/FishNet/Example/All/Prediction/Rigidbody/Prefabs/PredictedSphereSorta.prefab new file mode 100644 index 0000000..163cb9a --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Rigidbody/Prefabs/PredictedSphereSorta.prefab @@ -0,0 +1,246 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &3624261834906416581 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3624261834906416580} + - component: {fileID: 3624261834906416603} + - component: {fileID: 3624261834906416600} + - component: {fileID: 3624261834906416601} + - component: {fileID: 3624261834906416602} + - component: {fileID: -6246116784124330627} + - component: {fileID: 7850526870340082268} + m_Layer: 0 + m_Name: PredictedSphereSorta + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3624261834906416580 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3624261834906416581} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 15.312408, y: 6.390984, z: 25.2406} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 3624261835462115340} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!135 &3624261834906416603 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3624261834906416581} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!114 &3624261834906416600 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3624261834906416581} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 402926ef33e0a894d9fec352693988ac, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 3624261834906416601} + _networkObjectCache: {fileID: 3624261834906416601} + _implementsPredictionMethods: 0 + _graphicalObject: {fileID: 3624261835462115340} + _enableTeleport: 0 + _teleportThreshold: 200 + _ownerSmoothPosition: 1 + _ownerSmoothRotation: 1 + _ownerInterpolation: 1 + _predictionType: 1 + _rigidbody: {fileID: 3624261834906416602} + _rigidbody2d: {fileID: 0} + _spectatorSmoothPosition: 1 + _spectatorSmoothRotation: 1 + _spectatorInterpolation: 4 + _overflowMultiplier: 0.1 + _maintainedVelocity: 0 + _resendType: 0 + _resendInterval: 30 + _networkTransform: {fileID: 0} +--- !u!114 &3624261834906416601 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3624261834906416581} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + NetworkObserver: {fileID: 0} + k__BackingField: 34 + k__BackingField: 0 + _scenePathHash: 2281302723 + k__BackingField: 0 + k__BackingField: 12142845903367415924 + _sceneNetworkObjects: + - {fileID: 3624261834906416601} + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: {fileID: 7850526870340082268} + _networkBehaviours: + - {fileID: 3624261834906416600} + - {fileID: -6246116784124330627} + - {fileID: 7850526870340082268} + k__BackingField: {fileID: 0} + k__BackingField: [] + _isNetworked: 1 + _isGlobal: 0 + _initializeOrder: 0 + _defaultDespawnType: 0 +--- !u!54 &3624261834906416602 +Rigidbody: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3624261834906416581} + serializedVersion: 2 + m_Mass: 1 + m_Drag: 0 + m_AngularDrag: 0.05 + m_UseGravity: 0 + m_IsKinematic: 0 + m_Interpolate: 0 + m_Constraints: 0 + m_CollisionDetection: 0 +--- !u!114 &-6246116784124330627 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3624261834906416581} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fc7792d9dc338aa4dbc8a2f0533cf6ec, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 1 + _addedNetworkObject: {fileID: 3624261834906416601} + _networkObjectCache: {fileID: 3624261834906416601} + Force: 20 +--- !u!114 &7850526870340082268 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3624261834906416581} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e2b597e1828355a4d994a69cbb11ef85, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 2 + _addedNetworkObject: {fileID: 3624261834906416601} + _networkObjectCache: {fileID: 3624261834906416601} + _allowSpawning: 1 + _allowDespawning: 1 +--- !u!1 &3624261835462115341 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3624261835462115340} + - component: {fileID: 3624261835462115330} + - component: {fileID: 3624261835462115331} + m_Layer: 0 + m_Name: Graphics + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3624261835462115340 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3624261835462115341} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 3624261834906416580} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &3624261835462115330 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3624261835462115341} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &3624261835462115331 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3624261835462115341} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 diff --git a/Assets/FishNet/Example/All/Prediction/Rigidbody/Prefabs/PredictedSphereSorta.prefab.meta b/Assets/FishNet/Example/All/Prediction/Rigidbody/Prefabs/PredictedSphereSorta.prefab.meta new file mode 100644 index 0000000..b8038ee --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Rigidbody/Prefabs/PredictedSphereSorta.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 4e1673ccc2acac543871855a0e8bed71 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/Rigidbody/Prefabs/RigidbodyPrediction.prefab b/Assets/FishNet/Example/All/Prediction/Rigidbody/Prefabs/RigidbodyPrediction.prefab new file mode 100644 index 0000000..782c8e4 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Rigidbody/Prefabs/RigidbodyPrediction.prefab @@ -0,0 +1,478 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &303449597948771942 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 303449597948771941} + - component: {fileID: 303449597948771939} + - component: {fileID: 303449597948771940} + m_Layer: 0 + m_Name: Cube (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &303449597948771941 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 303449597948771942} + m_LocalRotation: {x: 0, y: 0.7071068, z: 0, w: 0.7071068} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1.5, y: 0.25, z: 0.25} + m_Children: [] + m_Father: {fileID: 303449599178274091} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 90, z: 0} +--- !u!33 &303449597948771939 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 303449597948771942} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &303449597948771940 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 303449597948771942} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!1 &303449598114786579 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 303449598114786577} + - component: {fileID: 303449598114786578} + - component: {fileID: 201277551} + - component: {fileID: 201277550} + - component: {fileID: 201277549} + - component: {fileID: 1565754455094126377} + m_Layer: 0 + m_Name: RigidbodyPrediction + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &303449598114786577 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 303449598114786579} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -4.80351, y: 0.18147132, z: 5.430528} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 303449599178274091} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!135 &303449598114786578 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 303449598114786579} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!114 &201277551 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 303449598114786579} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9e123f937d4b4f94c9cb6cc03944f786, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 201277550} + _networkObjectCache: {fileID: 201277550} + _jumpForce: 15 + _moveRate: 15 + BulletPrefab: {fileID: 3624261834906416601, guid: 4e1673ccc2acac543871855a0e8bed71, + type: 3} +--- !u!114 &201277550 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 303449598114786579} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + NetworkObserver: {fileID: 0} + k__BackingField: 2 + k__BackingField: 0 + _scenePathHash: 0 + k__BackingField: 0 + k__BackingField: 7596993593060998472 + _sceneNetworkObjects: [] + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: {fileID: 0} + _networkBehaviours: + - {fileID: 201277551} + - {fileID: 1565754455094126377} + k__BackingField: {fileID: 0} + k__BackingField: [] + _isNetworked: 1 + _isGlobal: 0 + _initializeOrder: 0 + _defaultDespawnType: 0 +--- !u!54 &201277549 +Rigidbody: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 303449598114786579} + serializedVersion: 2 + m_Mass: 1 + m_Drag: 0 + m_AngularDrag: 0.05 + m_UseGravity: 1 + m_IsKinematic: 0 + m_Interpolate: 0 + m_Constraints: 0 + m_CollisionDetection: 1 +--- !u!114 &1565754455094126377 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 303449598114786579} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 402926ef33e0a894d9fec352693988ac, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 1 + _addedNetworkObject: {fileID: 0} + _networkObjectCache: {fileID: 201277550} + _implementsPredictionMethods: 1 + _graphicalObject: {fileID: 303449599178274091} + _enableTeleport: 0 + _teleportThreshold: 1 + _ownerSmoothPosition: 1 + _ownerSmoothRotation: 1 + _ownerInterpolation: 1 + _predictionType: 1 + _rigidbody: {fileID: 201277549} + _rigidbody2d: {fileID: 0} + _spectatorSmoothPosition: 1 + _spectatorSmoothRotation: 1 + _spectatorSmoothingType: 1 + _customSmoothingData: + InterpolationPercent: 1 + CollisionInterpolationPercent: 0.1 + InterpolationDecreaseStep: 1 + InterpolationIncreaseStep: 3 + _preconfiguredSmoothingDataPreview: + InterpolationPercent: 1 + CollisionInterpolationPercent: 0.1 + InterpolationDecreaseStep: 1 + InterpolationIncreaseStep: 3 + _maintainedVelocity: 0 + _resendType: 0 + _resendInterval: 30 + _networkTransform: {fileID: 0} +--- !u!1 &303449598207636365 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 303449598207636364} + - component: {fileID: 303449598207636362} + - component: {fileID: 303449598207636363} + m_Layer: 0 + m_Name: Cube (2) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &303449598207636364 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 303449598207636365} + m_LocalRotation: {x: 0.5, y: 0.5, z: 0.5, w: 0.5} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1.5, y: 0.25, z: 0.25} + m_Children: [] + m_Father: {fileID: 303449599178274091} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 90, z: 90} +--- !u!33 &303449598207636362 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 303449598207636365} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &303449598207636363 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 303449598207636365} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!1 &303449598691742042 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 303449598691742041} + - component: {fileID: 303449598691742039} + - component: {fileID: 303449598691742040} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &303449598691742041 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 303449598691742042} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1.5, y: 0.25, z: 0.25} + m_Children: [] + m_Father: {fileID: 303449599178274091} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &303449598691742039 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 303449598691742042} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &303449598691742040 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 303449598691742042} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!1 &303449599178274092 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 303449599178274091} + - component: {fileID: 303449599178274088} + - component: {fileID: 303449599178274089} + m_Layer: 0 + m_Name: Sphere (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &303449599178274091 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 303449599178274092} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 303449598691742041} + - {fileID: 303449597948771941} + - {fileID: 303449598207636364} + m_Father: {fileID: 303449598114786577} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &303449599178274088 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 303449599178274092} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &303449599178274089 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 303449599178274092} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 diff --git a/Assets/FishNet/Example/All/Prediction/Rigidbody/Prefabs/RigidbodyPrediction.prefab.meta b/Assets/FishNet/Example/All/Prediction/Rigidbody/Prefabs/RigidbodyPrediction.prefab.meta new file mode 100644 index 0000000..1df735c --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Rigidbody/Prefabs/RigidbodyPrediction.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d904f93bc171bb144ba33c5155282f6f +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/Rigidbody/RigidbodyPrediction.unity b/Assets/FishNet/Example/All/Prediction/Rigidbody/RigidbodyPrediction.unity new file mode 100644 index 0000000..068f732 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Rigidbody/RigidbodyPrediction.unity @@ -0,0 +1,1517 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &192429403 stripped +GameObject: + m_CorrespondingSourceObject: {fileID: 7443408887813606051, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + m_PrefabInstance: {fileID: 1364334277} + m_PrefabAsset: {fileID: 0} +--- !u!114 &192429404 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 192429403} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3fdaae44044276a49a52229c1597e33b, type: 3} + m_Name: + m_EditorClassIdentifier: + _updateOrder: 0 + _timingType: 0 + _allowTickDropping: 0 + _maximumFrameTicks: 2 + _tickRate: 50 + _pingInterval: 1 + _timingInterval: 3 + _physicsMode: 1 +--- !u!114 &192429405 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 192429403} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6f48f002b825cbd45a19bd96d90f9edb, type: 3} + m_Name: + m_EditorClassIdentifier: + _unreliableMTU: 1023 + _ipv4BindAddress: + _ipv6BindAddress: + _port: 7770 + _maximumClients: 5000 + _clientAddress: localhost + _timeout: 15 +--- !u!114 &192429406 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 192429403} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f9b6b565cd9533c4ebc18003f0fc18a2, type: 3} + m_Name: + m_EditorClassIdentifier: + _color: {r: 1, g: 1, b: 1, a: 1} + _placement: 1 + _hideTickRate: 1 +--- !u!114 &192429407 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 192429403} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 34e4a322dca349547989b14021da4e23, type: 3} + m_Name: + m_EditorClassIdentifier: + Transport: {fileID: 192429405} + _intermediateLayer: {fileID: 0} + _latencySimulator: + _enabled: 0 + _simulateHost: 1 + _latency: 5 + _outOfOrder: 0 + _packetLoss: 0 +--- !u!114 &192429408 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 192429403} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 68828c85278210948b9d50a8db3aab74, type: 3} + m_Name: + m_EditorClassIdentifier: + _authenticator: {fileID: 0} + _syncTypeRate: 0.1 + SpawnPacking: + Position: 0 + Rotation: 2 + Scale: 2 + _changeFrameRate: 1 + _frameRate: 100 + _shareIds: 1 + _startOnHeadless: 1 + _limitClientMTU: 1 +--- !u!114 &192429409 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 192429403} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e08bb003fce297d4086cf8cba5aa459a, type: 3} + m_Name: + m_EditorClassIdentifier: + _dropExcessiveReplicates: 1 + _maximumServerReplicates: 15 + _maximumConsumeCount: 4 + _redundancyCount: 3 + _allowPredictedSpawning: 1 + _reservedObjectIds: 15 +--- !u!114 &192429410 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 192429403} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: aca43cf6f20e77c4f8fcc078fd85081f, type: 3} + m_Name: + m_EditorClassIdentifier: + _changeFrameRate: 1 + _frameRate: 100 +--- !u!1 &212039606 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 212039607} + - component: {fileID: 212039608} + m_Layer: 0 + m_Name: View + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &212039607 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 212039606} + m_LocalRotation: {x: 0.37959778, y: -0.5981522, z: 0.3830318, w: 0.5927952} + m_LocalPosition: {x: 10.585218, y: 24.346329, z: 20.032343} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 40, y: -90, z: 0} +--- !u!114 &212039608 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 212039606} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + NetworkObserver: {fileID: 0} + k__BackingField: 0 + k__BackingField: 0 + _scenePathHash: 2281302723 + k__BackingField: 9798120588264221620 + k__BackingField: 0 + _sceneNetworkObjects: + - {fileID: 212039608} + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: {fileID: 0} + _networkBehaviours: [] + k__BackingField: {fileID: 0} + k__BackingField: [] + _isNetworked: 1 + _isGlobal: 0 + _initializeOrder: 0 + _defaultDespawnType: 0 +--- !u!1 &555580081 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 555580085} + - component: {fileID: 555580084} + - component: {fileID: 555580083} + - component: {fileID: 555580082} + m_Layer: 0 + m_Name: Sphere + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!135 &555580082 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 555580081} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &555580083 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 555580081} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 6273444b68d517449aadb36abebaf561, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &555580084 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 555580081} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &555580085 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 555580081} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -9.16, y: -3.4, z: 13.1} + m_LocalScale: {x: 15, y: 15, z: 15} + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &587483120 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 587483126} + - component: {fileID: 587483125} + - component: {fileID: 587483124} + - component: {fileID: 587483123} + - component: {fileID: 587483122} + - component: {fileID: 587483121} + m_Layer: 0 + m_Name: OfflineCapsule + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!54 &587483121 +Rigidbody: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 587483120} + serializedVersion: 2 + m_Mass: 1 + m_Drag: 0 + m_AngularDrag: 0.05 + m_UseGravity: 1 + m_IsKinematic: 0 + m_Interpolate: 0 + m_Constraints: 0 + m_CollisionDetection: 0 +--- !u!114 &587483122 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 587483120} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b749f90d4c9961c4991179db1130fa4d, type: 3} + m_Name: + m_EditorClassIdentifier: + _rigidbodyType: 0 + _getInChildren: 0 +--- !u!136 &587483123 +CapsuleCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 587483120} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + m_Radius: 0.5 + m_Height: 2 + m_Direction: 1 + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &587483124 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 587483120} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &587483125 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 587483120} + m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &587483126 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 587483120} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 6.0329375, y: 0.01036527, z: 8.858636} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &872683029 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 872683031} + - component: {fileID: 872683030} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &872683030 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 872683029} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &872683031 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 872683029} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &967467089 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 967467090} + - component: {fileID: 967467093} + - component: {fileID: 967467092} + - component: {fileID: 967467091} + m_Layer: 0 + m_Name: Wall + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &967467090 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 967467089} + m_LocalRotation: {x: 0, y: 0.82565534, z: 0, w: 0.56417483} + m_LocalPosition: {x: 8.24, y: -0.08, z: 15.86} + m_LocalScale: {x: 1, y: 5, z: 5} + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 111.31, z: 0} +--- !u!65 &967467091 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 967467089} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &967467092 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 967467089} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 6273444b68d517449aadb36abebaf561, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &967467093 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 967467089} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1112005912 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1112005916} + - component: {fileID: 1112005915} + - component: {fileID: 1112005914} + - component: {fileID: 1112005913} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &1112005913 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1112005912} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1112005914 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1112005912} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 0bb31cf72dfcef449a1a4a5aab857f63, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &1112005915 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1112005912} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1112005916 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1112005912} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: -2, z: 0} + m_LocalScale: {x: 100, y: 1, z: 100} + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1256183191 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1256183192} + - component: {fileID: 1256183195} + - component: {fileID: 1256183194} + m_Layer: 0 + m_Name: Sphere + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1256183192 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1256183191} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1560902884} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &1256183194 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1256183191} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &1256183195 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1256183191} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1001 &1364334277 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 4393252310584637056, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: AutoStart + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4393252310584637056, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: _autoStartType + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606049, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_RootOrder + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606049, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606049, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606049, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606049, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606049, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606049, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606049, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606049, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606049, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606049, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606050, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: _spawnablePrefabs + value: + objectReference: {fileID: 11400000, guid: 3a54436bdb916194f99da0d17231e617, + type: 2} + - target: {fileID: 7443408887813606050, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: _refreshDefaultPrefabs + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606051, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_Name + value: NetworkManager + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606060, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: _playerPrefab + value: + objectReference: {fileID: 201277550, guid: d904f93bc171bb144ba33c5155282f6f, + type: 3} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 0b650fca685f2eb41a86538aa883e4c1, type: 3} +--- !u!1 &1377211188 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1377211192} + - component: {fileID: 1377211191} + - component: {fileID: 1377211190} + - component: {fileID: 1377211189} + m_Layer: 0 + m_Name: Sphere + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!135 &1377211189 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1377211188} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 0 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1377211190 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1377211188} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &1377211191 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1377211188} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1377211192 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1377211188} + m_LocalRotation: {x: 0, y: 0, z: 0.9122505, w: -0.40963268} + m_LocalPosition: {x: 10.983304, y: -0.99999994, z: 5.430528} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1470934487 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1470934491} + - component: {fileID: 1470934490} + - component: {fileID: 1470934489} + - component: {fileID: 1470934488} + m_Layer: 0 + m_Name: Wall + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &1470934488 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1470934487} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1470934489 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1470934487} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 6273444b68d517449aadb36abebaf561, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &1470934490 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1470934487} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1470934491 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1470934487} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 12.11, y: -0.08, z: 0} + m_LocalScale: {x: 1, y: 5, z: 20} + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1560902877 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1560902884} + - component: {fileID: 1560902881} + - component: {fileID: 1560902878} + - component: {fileID: 1560902880} + - component: {fileID: 1560902879} + m_Layer: 0 + m_Name: OnlineSphere + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!54 &1560902878 +Rigidbody: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1560902877} + serializedVersion: 2 + m_Mass: 1 + m_Drag: 0 + m_AngularDrag: 0.05 + m_UseGravity: 1 + m_IsKinematic: 0 + m_Interpolate: 0 + m_Constraints: 0 + m_CollisionDetection: 1 +--- !u!114 &1560902879 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1560902877} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 402926ef33e0a894d9fec352693988ac, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 1560902880} + _networkObjectCache: {fileID: 1560902880} + _implementsPredictionMethods: 1 + _graphicalObject: {fileID: 1256183192} + _enableTeleport: 0 + _teleportThreshold: 1 + _ownerSmoothPosition: 1 + _ownerSmoothRotation: 1 + _ownerInterpolation: 1 + _predictionType: 1 + _rigidbody: {fileID: 1560902878} + _rigidbody2d: {fileID: 0} + _spectatorSmoothPosition: 1 + _spectatorSmoothRotation: 1 + _spectatorSmoothingType: 1 + _maintainedVelocity: 0 + _resendType: 0 + _resendInterval: 30 + _networkTransform: {fileID: 0} +--- !u!114 &1560902880 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1560902877} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + NetworkObserver: {fileID: 0} + k__BackingField: 65535 + k__BackingField: 0 + _scenePathHash: 2281302723 + k__BackingField: 9798120589401593003 + k__BackingField: 0 + _sceneNetworkObjects: + - {fileID: 212039608} + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: {fileID: 0} + _networkBehaviours: + - {fileID: 1560902879} + k__BackingField: {fileID: 0} + k__BackingField: [] + _isNetworked: 1 + _isGlobal: 0 + _initializeOrder: 0 + _defaultDespawnType: 0 +--- !u!135 &1560902881 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1560902877} + m_Material: {fileID: 13400000, guid: 2315b89c915a21c40982b5867ff7d343, type: 2} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!4 &1560902884 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1560902877} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 1.1, y: 0, z: 5.430528} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1256183192} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1784594014 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1784594015} + m_Layer: 0 + m_Name: Level + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1784594015 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1784594014} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1112005916} + - {fileID: 1470934491} + - {fileID: 555580085} + - {fileID: 1852016427} + - {fileID: 872683031} + - {fileID: 967467090} + - {fileID: 1883869627} + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1852016424 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1852016427} + - component: {fileID: 1852016426} + - component: {fileID: 1852016425} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &1852016425 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1852016424} + m_Enabled: 1 +--- !u!20 &1852016426 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1852016424} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &1852016427 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1852016424} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1883869626 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1883869627} + - component: {fileID: 1883869630} + - component: {fileID: 1883869629} + - component: {fileID: 1883869628} + m_Layer: 0 + m_Name: Cube (3) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1883869627 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1883869626} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -48.62, y: -0.08, z: 0} + m_LocalScale: {x: 1, y: 5, z: 20} + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1883869628 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1883869626} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1883869629 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1883869626} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 6273444b68d517449aadb36abebaf561, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &1883869630 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1883869626} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} diff --git a/Assets/FishNet/Example/All/Prediction/Rigidbody/RigidbodyPrediction.unity.meta b/Assets/FishNet/Example/All/Prediction/Rigidbody/RigidbodyPrediction.unity.meta new file mode 100644 index 0000000..2edff22 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Rigidbody/RigidbodyPrediction.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 005289ca292137448ba5edc0e1163f4a +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/Rigidbody/Scripts.meta b/Assets/FishNet/Example/All/Prediction/Rigidbody/Scripts.meta new file mode 100644 index 0000000..f4324aa --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Rigidbody/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 789e9c92c35f6aa4bb179f6ee73a3061 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/Rigidbody/Scripts/BulletTest.cs b/Assets/FishNet/Example/All/Prediction/Rigidbody/Scripts/BulletTest.cs new file mode 100644 index 0000000..824ea81 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Rigidbody/Scripts/BulletTest.cs @@ -0,0 +1,59 @@ +using FishNet; +using FishNet.Object; +using FishNet.Object.Synchronizing; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class BulletTest : NetworkBehaviour +{ + [SyncObject] + public readonly SyncList SyncListNumbers = new SyncList(); + + [SyncVar] + public Vector3 SyncVarScale = Vector3.one; + + public float Force; + + public void SendSpeed() + { + //Debug.Log("Local tick " + InstanceFinder.TimeManager.LocalTick); + Rigidbody rb = GetComponent(); + rb.AddForce((transform.forward * Force), ForceMode.Impulse); + } + + public override void OnStartNetwork() + { + base.OnStartNetwork(); + transform.localScale = SyncVarScale; + + Debug.Log("Reading numbers"); + foreach (var item in SyncListNumbers) + Debug.Log(item); + + } + + public override void OnStartServer() + { + base.OnStartServer(); + SendSpeed(); + StartCoroutine(__DelayDestroy(3f)); + } + + private void Update() + { + if (base.TimeManager == null) + return; + if (base.TimeManager.FrameTicked) + { +// Debug.Log(_scale); + //Rigidbody rb = GetComponent(); + //Debug.Log(rb.velocity.z); + } + } + private IEnumerator __DelayDestroy(float time) + { + yield return new WaitForSeconds(time); + base.Despawn(); + } +} diff --git a/Assets/FishNet/Example/All/Prediction/Rigidbody/Scripts/BulletTest.cs.meta b/Assets/FishNet/Example/All/Prediction/Rigidbody/Scripts/BulletTest.cs.meta new file mode 100644 index 0000000..338a0b6 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Rigidbody/Scripts/BulletTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fc7792d9dc338aa4dbc8a2f0533cf6ec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/Rigidbody/Scripts/PredictedBullet.cs b/Assets/FishNet/Example/All/Prediction/Rigidbody/Scripts/PredictedBullet.cs new file mode 100644 index 0000000..207da52 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Rigidbody/Scripts/PredictedBullet.cs @@ -0,0 +1,106 @@ +using FishNet.Component.Prediction; +using FishNet.Object; +using FishNet.Object.Synchronizing; +using System.Collections; +using UnityEngine; + +public class PredictedBullet : NetworkBehaviour +{ + //SyncVar to set spawn force. This is set by predicted spawner and sent to the server. + [SyncVar(OnChange = nameof(_startingForce_OnChange))] + private Vector3 _startingForce; + //Tick to set rb to kinematic. + private uint _stopTick = uint.MaxValue; + + public void SetStartingForce(Vector3 value) + { + /* If the object is not yet initialized then + * this is being set prior to network spawning. + * Such a scenario occurs because the client which is + * predicted spawning sets the synctype value before network + * spawning to ensure the server receives the value. + * Just as when the server sets synctypes, if they are set + * before the object is spawned it's gauranteed clients will + * get the value in the spawn packet; same practice is used here. */ + if (!base.IsSpawned) + SetVelocity(value); + + _startingForce = value; + } + + //Simple delay destroy so object does not exist forever. + public override void OnStartServer() + { + base.OnStartServer(); + StartCoroutine(__DelayDestroy(3f)); + } + + public override void OnStartNetwork() + { + base.OnStartNetwork(); + uint timeToTicks = base.TimeManager.TimeToTicks(0.65f); + /* If server or predicted spawner then add the kinematic + * tick onto local. Predicted spawner and server should behave + * as though no time has elapsed since this spawned. */ + if (base.IsServer || base.Owner.IsLocalClient) + { + _stopTick = base.TimeManager.LocalTick + timeToTicks; + } + /* If not server or a client that did not predicted spawn this + * then calculate time passed and set kinematic tick to the same + * amount in the future while subtracting already passed ticks. */ + else + { + uint passed = (uint)Mathf.Max(1, base.TimeManager.Tick - base.TimeManager.LastPacketTick); + long stopTick = (base.TimeManager.Tick + timeToTicks - passed - 1); + if (stopTick > 0) + _stopTick = (uint)stopTick; + else + _stopTick = 0; + } + + base.TimeManager.OnTick += TimeManager_OnTick; + } + + public override void OnStopNetwork() + { + base.OnStopNetwork(); + base.TimeManager.OnTick -= TimeManager_OnTick; + } + private void TimeManager_OnTick() + { + if (base.TimeManager.LocalTick >= _stopTick) + { + Rigidbody rb = GetComponent(); + rb.isKinematic = true; + } + } + + /// + /// When starting force changes set that velocity to the rigidbody. + /// This is an example as how a predicted spawn can modify sync types for server and other clients. + /// + private void _startingForce_OnChange(Vector3 prev, Vector3 next, bool asServer) + { + SetVelocity(next); + } + + /// + /// Sets velocity of the rigidbody. + /// + public void SetVelocity(Vector3 value) + { + Rigidbody rb = GetComponent(); + rb.velocity = value; + } + + /// + /// Destroy object after time. + /// + private IEnumerator __DelayDestroy(float time) + { + yield return new WaitForSeconds(time); + base.Despawn(); + } + +} diff --git a/Assets/FishNet/Example/All/Prediction/Rigidbody/Scripts/PredictedBullet.cs.meta b/Assets/FishNet/Example/All/Prediction/Rigidbody/Scripts/PredictedBullet.cs.meta new file mode 100644 index 0000000..bd4077f --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Rigidbody/Scripts/PredictedBullet.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2878f245b0c06ce49b3e98d5984e8d41 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/Rigidbody/Scripts/RigidbodyPrediction.cs b/Assets/FishNet/Example/All/Prediction/Rigidbody/Scripts/RigidbodyPrediction.cs new file mode 100644 index 0000000..ee3fe6e --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Rigidbody/Scripts/RigidbodyPrediction.cs @@ -0,0 +1,285 @@ +using FishNet; +using FishNet.Object; +using FishNet.Object.Prediction; +using FishNet.Transporting; +using UnityEngine; + +/* +* +* See TransformPrediction.cs for more detailed notes. +* +*/ + +namespace FishNet.Example.Prediction.Rigidbodies +{ + + public class RigidbodyPrediction : NetworkBehaviour + { + #region Types. + public struct MoveData : IReplicateData + { + public bool Jump; + public float Horizontal; + public float Vertical; + public MoveData(bool jump, float horizontal, float vertical) + { + Jump = jump; + Horizontal = horizontal; + Vertical = vertical; + _tick = 0; + } + + private uint _tick; + public void Dispose() { } + public uint GetTick() => _tick; + public void SetTick(uint value) => _tick = value; + } + public struct ReconcileData : IReconcileData + { + public Vector3 Position; + public Quaternion Rotation; + public Vector3 Velocity; + public Vector3 AngularVelocity; + public ReconcileData(Vector3 position, Quaternion rotation, Vector3 velocity, Vector3 angularVelocity) + { + Position = position; + Rotation = rotation; + Velocity = velocity; + AngularVelocity = angularVelocity; + _tick = 0; + } + + private uint _tick; + public void Dispose() { } + public uint GetTick() => _tick; + public void SetTick(uint value) => _tick = value; + } + #endregion + + #region Serialized. + [SerializeField] + private float _jumpForce = 15f; + [SerializeField] + private float _moveRate = 15f; + #endregion + + #region Private. + /// + /// Rigidbody on this object. + /// + private Rigidbody _rigidbody; + /// + /// Next time a jump is allowed. + /// + private float _nextJumpTime; + /// + /// True to jump next frame. + /// + private bool _jump; + #endregion + + #region Predicted spawning. + /// + /// Prefab to spawn for predicted spawning. + /// + public NetworkObject BulletPrefab; + /// + /// True if a spawn is queued from input. + /// + private bool _spawnBullet; + /// + /// True if a despawn is queued from input. + /// + private bool _despawnBullet; + /// + /// Last spawned bullet. Used to test predicted despawn. + /// + private NetworkObject _lastSpawnedBullet; + #endregion + + private void Awake() + { + + _rigidbody = GetComponent(); + InstanceFinder.TimeManager.OnTick += TimeManager_OnTick; + InstanceFinder.TimeManager.OnPostTick += TimeManager_OnPostTick; + } + + private void OnDestroy() + { + if (InstanceFinder.TimeManager != null) + { + InstanceFinder.TimeManager.OnTick -= TimeManager_OnTick; + InstanceFinder.TimeManager.OnPostTick -= TimeManager_OnPostTick; + } + } + + public override void OnStartClient() + { + base.OnStartClient(); + base.PredictionManager.OnPreReplicateReplay += PredictionManager_OnPreReplicateReplay; + } + public override void OnStopClient() + { + base.OnStopClient(); + base.PredictionManager.OnPreReplicateReplay -= PredictionManager_OnPreReplicateReplay; + } + + private void Update() + { + if (base.IsOwner) + { + if (Input.GetKeyDown(KeyCode.RightAlt)) + { + _rigidbody.velocity = Vector3.zero; + _rigidbody.angularVelocity = Vector3.zero; + } + if (Input.GetKeyDown(KeyCode.Space) && Time.time > _nextJumpTime) + { + _nextJumpTime = Time.time + 1f; + _jump = true; + } + else if (Input.GetKeyDown(KeyCode.LeftShift)) + { + _spawnBullet = true; + } + //else if (Input.GetKeyDown(KeyCode.LeftAlt)) + //{ + // _despawnBullet = true; + //} + } + } + + /// + /// Called every time any predicted object is replaying. Replays only occur for owner. + /// Currently owners may only predict one object at a time. + /// + private void PredictionManager_OnPreReplicateReplay(uint arg1, PhysicsScene arg2, PhysicsScene2D arg3) + { + /* Server does not replay so it does + * not need to add gravity. */ + if (!base.IsServer) + AddGravity(); + } + + + private void TimeManager_OnTick() + { + if (base.IsOwner) + { + Reconciliation(default, false); + BuildMoveData(out MoveData md); + Move(md, false); + //Predicted spawning example. + TryDespawnBullet(); + TrySpawnBullet(); + } + if (base.IsServer) + { + Move(default, true); + } + + /* Server and all clients must add the additional gravity. + * Adding gravity is not necessarily required in general but + * to make jumps more snappy extra gravity is added per tick. + * All clients and server need to simulate the gravity to keep + * prediction equal across the network. */ + AddGravity(); + } + + private void TimeManager_OnPostTick() + { + /* Reconcile is sent during PostTick because we + * want to send the rb data AFTER the simulation. */ + if (base.IsServer) + { + ReconcileData rd = new ReconcileData(transform.position, transform.rotation, _rigidbody.velocity, _rigidbody.angularVelocity); + Reconciliation(rd, true); + } + } + + + /// + /// Builds a MoveData to use within replicate. + /// + /// + private void BuildMoveData(out MoveData md) + { + md = default; + + float horizontal = Input.GetAxisRaw("Horizontal"); + float vertical = Input.GetAxisRaw("Vertical"); + + if (horizontal == 0f && vertical == 0f && !_jump) + return; + + md = new MoveData(_jump, horizontal, vertical); + _jump = false; + } + + + /// + /// PredictedObject example (unpolished) + /// + private void TrySpawnBullet() + { + if (_spawnBullet) + { + _spawnBullet = false; + + NetworkObject nob = Instantiate(BulletPrefab, transform.position + (transform.forward * 1f), transform.rotation); + //Set last spawned to test destroy with ALT key. + _lastSpawnedBullet = nob; + + //Set force to 100f at current forward. + PredictedBullet bt = nob.GetComponent(); + bt.SetStartingForce(transform.forward * 20f); + //Spawn client side, which will send the predicted spawn to server. + base.Spawn(nob, base.Owner); + } + } + + /// + /// PredictedObject example (unpolished) + /// + private void TryDespawnBullet() + { + if (_despawnBullet) + { + _despawnBullet = false; + _lastSpawnedBullet?.Despawn(); + } + } + + /// + /// Adds gravity to the rigidbody. + /// + private void AddGravity() + { + _rigidbody.AddForce(Physics.gravity * 2f); + } + + [Replicate] + private void Move(MoveData md, bool asServer, Channel channel = Channel.Unreliable, bool replaying = false) + { + Vector3 forces = new Vector3(md.Horizontal, 0f, md.Vertical) * _moveRate; + _rigidbody.AddForce(forces); + + if (md.Jump) + _rigidbody.AddForce(new Vector3(0f, _jumpForce, 0f), ForceMode.Impulse); + } + + [Reconcile] + private void Reconciliation(ReconcileData rd, bool asServer, Channel channel = Channel.Unreliable) + { + transform.position = rd.Position; + transform.rotation = rd.Rotation; + _rigidbody.velocity = rd.Velocity; + _rigidbody.angularVelocity = rd.AngularVelocity; + } + + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Example/All/Prediction/Rigidbody/Scripts/RigidbodyPrediction.cs.meta b/Assets/FishNet/Example/All/Prediction/Rigidbody/Scripts/RigidbodyPrediction.cs.meta new file mode 100644 index 0000000..b80d6e4 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Rigidbody/Scripts/RigidbodyPrediction.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9e123f937d4b4f94c9cb6cc03944f786 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/Transform.meta b/Assets/FishNet/Example/All/Prediction/Transform.meta new file mode 100644 index 0000000..af337f4 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Transform.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 49d8c9756de86004c802a5ce8448fdad +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/Transform/Prefabs.meta b/Assets/FishNet/Example/All/Prediction/Transform/Prefabs.meta new file mode 100644 index 0000000..abbd684 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Transform/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 52874c2b0ae3cc74d9b6be4650f3a24a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/Transform/Prefabs/TransformPrediction.prefab b/Assets/FishNet/Example/All/Prediction/Transform/Prefabs/TransformPrediction.prefab new file mode 100644 index 0000000..26c013c --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Transform/Prefabs/TransformPrediction.prefab @@ -0,0 +1,250 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &27039729695437544 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 27039729695437538} + - component: {fileID: 27039729695437541} + - component: {fileID: 27039729695437542} + - component: {fileID: 27039729695437543} + - component: {fileID: 8399567158492483659} + - component: {fileID: -4118712540632748398} + m_Layer: 0 + m_Name: TransformPrediction + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &27039729695437538 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 27039729695437544} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 7846666075604890595} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!136 &27039729695437541 +CapsuleCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 27039729695437544} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + m_Radius: 0.5 + m_Height: 2 + m_Direction: 1 + m_Center: {x: 0, y: 0, z: 0} +--- !u!114 &27039729695437542 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 27039729695437544} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 13cad243fdfd7294e8a6a393735726eb, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 255 + _addedNetworkObject: {fileID: 27039729695437543} + _networkObjectCache: {fileID: 0} + _moveRate: 5 +--- !u!114 &27039729695437543 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 27039729695437544} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + NetworkObserver: {fileID: 0} + k__BackingField: 0 + k__BackingField: 0 + _scenePathHash: 0 + k__BackingField: 0 + k__BackingField: 139770397801111289 + _sceneNetworkObjects: [] + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: {fileID: 0} + _networkBehaviours: [] + k__BackingField: {fileID: 0} + k__BackingField: [] + _isNetworked: 1 + _isGlobal: 0 + _initializeOrder: 0 + _defaultDespawnType: 0 +--- !u!114 &8399567158492483659 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 27039729695437544} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 402926ef33e0a894d9fec352693988ac, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 255 + _addedNetworkObject: {fileID: 0} + _networkObjectCache: {fileID: 0} + _implementsPredictionMethods: 1 + _graphicalObject: {fileID: 0} + _enableTeleport: 0 + _teleportThreshold: 1 + _ownerSmoothPosition: 1 + _ownerSmoothRotation: 1 + _ownerInterpolation: 1 + _predictionType: 0 + _rigidbody: {fileID: 0} + _rigidbody2d: {fileID: 0} + _spectatorSmoothPosition: 1 + _spectatorSmoothRotation: 1 + _spectatorInterpolation: 4 + _overflowMultiplier: 0.1 + _maintainedVelocity: 0 + _resendType: 0 + _resendInterval: 30 + _networkTransform: {fileID: 0} +--- !u!114 &-4118712540632748398 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 27039729695437544} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a2836e36774ca1c4bbbee976e17b649c, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 255 + _addedNetworkObject: {fileID: 27039729695437543} + _networkObjectCache: {fileID: 0} + _synchronizeParent: 0 + _packing: + Position: 1 + Rotation: 1 + Scale: 0 + _interpolation: 2 + _extrapolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + _clientAuthoritative: 1 + _sendToOwner: 1 + _synchronizePosition: 1 + _positionSnapping: + X: 0 + Y: 0 + Z: 0 + _synchronizeRotation: 1 + _rotationSnapping: + X: 0 + Y: 0 + Z: 0 + _synchronizeScale: 1 + _scaleSnapping: + X: 0 + Y: 0 + Z: 0 +--- !u!1 &907868826061293566 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7846666075604890595} + - component: {fileID: 3240451035359699034} + - component: {fileID: 85305618895715439} + m_Layer: 0 + m_Name: Capsule + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7846666075604890595 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 907868826061293566} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 27039729695437538} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &3240451035359699034 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 907868826061293566} + m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &85305618895715439 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 907868826061293566} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 diff --git a/Assets/FishNet/Example/All/Prediction/Transform/Prefabs/TransformPrediction.prefab.meta b/Assets/FishNet/Example/All/Prediction/Transform/Prefabs/TransformPrediction.prefab.meta new file mode 100644 index 0000000..6ef3a43 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Transform/Prefabs/TransformPrediction.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f820efff6a2871447b961fc755212ba3 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/Transform/Scripts.meta b/Assets/FishNet/Example/All/Prediction/Transform/Scripts.meta new file mode 100644 index 0000000..a24d6b2 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Transform/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 99b192b05caa072458c66dbd47086403 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/Transform/Scripts/TransformPrediction.cs b/Assets/FishNet/Example/All/Prediction/Transform/Scripts/TransformPrediction.cs new file mode 100644 index 0000000..e56d456 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Transform/Scripts/TransformPrediction.cs @@ -0,0 +1,222 @@ +using FishNet.Object; +using FishNet.Object.Prediction; +using FishNet.Transporting; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace FishNet.Example.Prediction.Transforms +{ + public class TransformPrediction : NetworkBehaviour + { + /// + /// Data on how to move. + /// This is processed locally as well sent to the server for processing. + /// Any inputs or values which may affect your move should be placed in your own MoveData. + /// The structure type may be named anything. Classes can also be used but will generate garbage, so structures + /// are recommended. + /// + public struct TestStruct + { + public int Two; + } + public class TestClass + { + public int One; + } + public struct MoveData : IReplicateData + { + public Vector2 Movement; + //public TestStruct _testStruct; + //public TestClass _testClass; + public float Horizontal; + public float Vertical; + private uint _tick; + + public MoveData(float horizontal, float vertical) + { + Movement = new Vector2(horizontal, vertical); + Horizontal = horizontal; + Vertical = vertical; + _tick = 0; + //_testStruct = default; + //_testClass = null; + } + + public void Dispose() { } + public uint GetTick() => _tick; + public void SetTick(uint value) => _tick = value; + } + + /// + /// Data on how to reconcile. + /// Server sends this back to the client. Once the client receives this they + /// will reset their object using this information. Like with MoveData anything that may + /// affect your movement should be reset. Since this is just a transform only position and + /// rotation would be reset. But a rigidbody would include velocities as well. If you are using + /// an asset it's important to know what systems in that asset affect movement and need + /// to be reset as well. + /// + public struct ReconcileData : IReconcileData + { + public Vector3 Position; + public Quaternion Rotation; + private uint _tick; + + public ReconcileData(Vector3 position, Quaternion rotation) + { + Position = position; + Rotation = rotation; + _tick = 0; + } + + public void Dispose() { } + public uint GetTick() => _tick; + public void SetTick(uint value) => _tick = value; + } + + #region Serialized. + /// + /// How many units to move per second. + /// + [Tooltip("How many units to move per second.")] + [SerializeField] + private float _moveRate = 5f; + #endregion + + private void Awake() + { + /* Prediction is tick based so you must + * send datas during ticks. You can use whichever + * tick best fits your need, such as PreTick, Tick, or PostTick. + * In most cases you will send/move using Tick. For rigidbodies + * you will send using PostTick. I subscribe to ticks using + * the InstanceFinder class, which finds the first NetworkManager + * loaded. If you are using several NetworkManagers you would want + * to subscrube in OnStartServer/Client using base.TimeManager. */ + InstanceFinder.TimeManager.OnTick += TimeManager_OnTick; + } + + private void OnDestroy() + { + //Unsubscribe as well. + if (InstanceFinder.TimeManager != null) + { + InstanceFinder.TimeManager.OnTick -= TimeManager_OnTick; + } + } + + private void TimeManager_OnTick() + { + if (base.IsOwner) + { + /* Call reconcile using default, and false for + * asServer. This will reset the client to the latest + * values from server and replay cached inputs. */ + Reconciliation(default, false); + /* CheckInput builds MoveData from user input. When there + * is no input CheckInput returns default. You can handle this + * however you like but Move should be called when default if + * there is no input which needs to be sent to the server. */ + CheckInput(out MoveData md); + /* Move using the input, and false for asServer. + * Inputs are automatically sent with redundancy. How many past + * inputs will be configurable at a later time. + * When a default value is used the most recent past inputs + * are sent a predetermined amount of times. It's important you + * call Move whether your data is default or not. FishNet will + * automatically determine how to send the data, and run the logic. */ + Move(md, false); + } + if (base.IsServer) + { + /* Move using default data with true for asServer. + * The server will use stored data from the client automatically. + * You may also run any sanity checks on the input as demonstrated + * in the method. */ + Move(default, true); + /* After the server has processed input you will want to send + * the result back to clients. You are welcome to skip + * a few sends if you like, eg only send every few ticks. + * Generate data required on how the client will reset and send it by calling your Reconcile + * method with the data, again using true for asServer. Like the + * Replicate method (Move) this will send with redundancy a certain + * amount of times. If there is no input to process from the client this + * will not continue to send data. */ + ReconcileData rd = new ReconcileData(transform.position, transform.rotation); + Reconciliation(rd, true); + } + } + + + /// + /// A simple method to get input. This doesn't have any relation to the prediction. + /// + private void CheckInput(out MoveData md) + { + md = default; + + float horizontal = Input.GetAxisRaw("Horizontal"); + float vertical = Input.GetAxisRaw("Vertical"); + + //No input to send. + if (horizontal == 0f && vertical == 0f) + return; + + //Make movedata with input. + md = new MoveData(horizontal, vertical); + } + + /// + /// Replicate attribute indicates the data is being sent from the client to the server. + /// When Replicate is present data is automatically sent with redundancy. + /// The replay parameter becomes true automatically when client inputs are + /// being replayed after a reconcile. This is useful for a variety of things, + /// such as if you only want to show effects the first time input is run you will + /// do so when replaying is false. + /// + [Replicate] + private void Move(MoveData md, bool asServer, Channel channel = Channel.Unreliable, bool replaying = false) + { + /* You can check if being run as server to + * add security checks such as normalizing + * the inputs. */ + if (asServer) + { + //Sanity check! + } + /* You may also use replaying to know + * if a client is replaying inputs rather + * than running them for the first time. This can + * be useful because you may only want to run + * VFX during the first input and not during + * replayed inputs. */ + if (!replaying) + { + //VFX! + } + + Vector3 move = new Vector3(md.Horizontal, 0f, md.Vertical); + transform.position += (move * _moveRate * (float)base.TimeManager.TickDelta); + } + + /// + /// A Reconcile attribute indicates the client will reconcile + /// using the data and logic within the method. When asServer + /// is true the data is sent to the client with redundancy, + /// and the server will not run the logic. + /// When asServer is false the client will reset using the logic + /// you supply then replay their inputs. + /// + [Reconcile] + private void Reconciliation(ReconcileData rd, bool asServer, Channel channel = Channel.Unreliable) + { + transform.position = rd.Position; + transform.rotation = rd.Rotation; + } + + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Example/All/Prediction/Transform/Scripts/TransformPrediction.cs.meta b/Assets/FishNet/Example/All/Prediction/Transform/Scripts/TransformPrediction.cs.meta new file mode 100644 index 0000000..01cb9f1 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Transform/Scripts/TransformPrediction.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 13cad243fdfd7294e8a6a393735726eb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/Prediction/Transform/TransformPrediction.unity b/Assets/FishNet/Example/All/Prediction/Transform/TransformPrediction.unity new file mode 100644 index 0000000..d57c7ad --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Transform/TransformPrediction.unity @@ -0,0 +1,890 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &192429403 stripped +GameObject: + m_CorrespondingSourceObject: {fileID: 7443408887813606051, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + m_PrefabInstance: {fileID: 1364334277} + m_PrefabAsset: {fileID: 0} +--- !u!114 &192429404 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 192429403} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3fdaae44044276a49a52229c1597e33b, type: 3} + m_Name: + m_EditorClassIdentifier: + _updateOrder: 0 + _timingType: 0 + _allowTickDropping: 0 + _maximumFrameTicks: 2 + _tickRate: 20 + _pingInterval: 15 + _timingInterval: 15 + _physicsMode: 1 +--- !u!114 &192429405 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 192429403} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6f48f002b825cbd45a19bd96d90f9edb, type: 3} + m_Name: + m_EditorClassIdentifier: + _unreliableMTU: 1023 + _ipv4BindAddress: + _ipv6BindAddress: + _port: 7770 + _maximumClients: 4095 + _clientAddress: localhost + _timeout: 15 +--- !u!114 &192429406 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 192429403} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 756c28cd3141c4140ae776188ee26729, type: 3} + m_Name: + m_EditorClassIdentifier: + NetworkTraffic: + _updateInteval: 1 + _updateClient: 1 + _updateServer: 1 +--- !u!114 &192429407 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 192429403} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8bc8f0363ddc75946a958043c5e49a83, type: 3} + m_Name: + m_EditorClassIdentifier: + _color: {r: 0, g: 0, b: 0, a: 1} + _placement: 2 + _showOutgoing: 1 + _showIncoming: 1 +--- !u!114 &192429408 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 192429403} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 34e4a322dca349547989b14021da4e23, type: 3} + m_Name: + m_EditorClassIdentifier: + Transport: {fileID: 192429405} + _latencySimulator: + _enabled: 1 + _simulateHost: 1 + _latency: 100 + _outOfOrder: 0 + _packetLoss: 0 +--- !u!114 &192429409 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 192429403} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e08bb003fce297d4086cf8cba5aa459a, type: 3} + m_Name: + m_EditorClassIdentifier: + _dropExcessiveReplicates: 1 + _maximumServerReplicates: 15 + _maximumConsumeCount: 4 + _redundancyCount: 3 + _allowPredictedSpawning: 0 + _reservedObjectIds: 15 +--- !u!1 &439822901 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 439822904} + - component: {fileID: 439822903} + - component: {fileID: 439822902} + m_Layer: 0 + m_Name: GameObject + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &439822902 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 439822901} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + NetworkObserver: {fileID: 0} + k__BackingField: 0 + k__BackingField: 0 + _scenePathHash: 3820541331 + k__BackingField: 16409100072611719970 + k__BackingField: 0 + _sceneNetworkObjects: + - {fileID: 439822902} + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: {fileID: 0} + _networkBehaviours: + - {fileID: 439822903} + k__BackingField: {fileID: 0} + k__BackingField: [] + _isNetworked: 1 + _isGlobal: 0 + _initializeOrder: 0 + _defaultDespawnType: 0 +--- !u!114 &439822903 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 439822901} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fc498684834f8864896da013f42864d9, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 439822902} + _networkObjectCache: {fileID: 439822902} +--- !u!4 &439822904 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 439822901} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -54.301113, y: 11.982289, z: 55.36256} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &555580081 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 555580085} + - component: {fileID: 555580084} + - component: {fileID: 555580083} + - component: {fileID: 555580082} + m_Layer: 0 + m_Name: Sphere + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!135 &555580082 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 555580081} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &555580083 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 555580081} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: ea31fef5ca1bc7344a72c71a5f9a0cd2, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &555580084 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 555580081} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &555580085 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 555580081} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -9.16, y: -3.4, z: 13.1} + m_LocalScale: {x: 15, y: 15, z: 15} + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &872683029 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 872683031} + - component: {fileID: 872683030} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &872683030 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 872683029} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &872683031 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 872683029} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &1112005912 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1112005916} + - component: {fileID: 1112005915} + - component: {fileID: 1112005914} + - component: {fileID: 1112005913} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &1112005913 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1112005912} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1112005914 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1112005912} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 0bb31cf72dfcef449a1a4a5aab857f63, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &1112005915 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1112005912} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1112005916 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1112005912} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: -2, z: 0} + m_LocalScale: {x: 100, y: 1, z: 100} + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1001 &1364334277 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 4393252310584637056, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: _autoStartType + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606049, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_RootOrder + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606049, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606049, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606049, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606049, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606049, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606049, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606049, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606049, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606049, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606049, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606050, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: _spawnablePrefabs + value: + objectReference: {fileID: 11400000, guid: 3a54436bdb916194f99da0d17231e617, + type: 2} + - target: {fileID: 7443408887813606051, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: m_Name + value: NetworkManager + objectReference: {fileID: 0} + - target: {fileID: 7443408887813606060, guid: 0b650fca685f2eb41a86538aa883e4c1, + type: 3} + propertyPath: _playerPrefab + value: + objectReference: {fileID: 27039729695437543, guid: f820efff6a2871447b961fc755212ba3, + type: 3} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 0b650fca685f2eb41a86538aa883e4c1, type: 3} +--- !u!1 &1470934487 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1470934491} + - component: {fileID: 1470934490} + - component: {fileID: 1470934489} + - component: {fileID: 1470934488} + m_Layer: 0 + m_Name: Cube (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &1470934488 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1470934487} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1470934489 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1470934487} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: ea31fef5ca1bc7344a72c71a5f9a0cd2, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &1470934490 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1470934487} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1470934491 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1470934487} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 12.11, y: -0.08, z: 0} + m_LocalScale: {x: 1, y: 5, z: 20} + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1784594014 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1784594015} + m_Layer: 0 + m_Name: Level + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1784594015 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1784594014} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1112005916} + - {fileID: 1470934491} + - {fileID: 555580085} + - {fileID: 1852016427} + - {fileID: 872683031} + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1852016424 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1852016427} + - component: {fileID: 1852016426} + - component: {fileID: 1852016425} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &1852016425 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1852016424} + m_Enabled: 1 +--- !u!20 &1852016426 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1852016424} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &1852016427 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1852016424} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Assets/FishNet/Example/All/Prediction/Transform/TransformPrediction.unity.meta b/Assets/FishNet/Example/All/Prediction/Transform/TransformPrediction.unity.meta new file mode 100644 index 0000000..f444295 --- /dev/null +++ b/Assets/FishNet/Example/All/Prediction/Transform/TransformPrediction.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 23d8c720c2338a54c9787668c81290f6 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/SceneManager.meta b/Assets/FishNet/Example/All/SceneManager.meta new file mode 100644 index 0000000..5a149c0 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3ce919ae77eaee5459c4c0b47a7c13f9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/SceneManager/Materials.meta b/Assets/FishNet/Example/All/SceneManager/Materials.meta new file mode 100644 index 0000000..dd18bf4 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Materials.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e72a8e512b2ca5142857eaab157e8f03 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/SceneManager/Materials/Black.mat b/Assets/FishNet/Example/All/SceneManager/Materials/Black.mat new file mode 100644 index 0000000..99f4278 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Materials/Black.mat @@ -0,0 +1,78 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Black + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: _ALPHAPREMULTIPLY_ON + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: 3000 + stringTagMap: + RenderType: Transparent + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 10 + - _GlossMapScale: 1 + - _Glossiness: 0 + - _GlossyReflections: 1 + - _Metallic: 0.177 + - _Mode: 3 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 0 + m_Colors: + - _Color: {r: 0, g: 0, b: 0, a: 0.39215687} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/Assets/FishNet/Example/All/SceneManager/Materials/Black.mat.meta b/Assets/FishNet/Example/All/SceneManager/Materials/Black.mat.meta new file mode 100644 index 0000000..b7dfa60 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Materials/Black.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2e7517d1496ae784f94a2307a88e2bb5 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/SceneManager/Materials/Blue.mat b/Assets/FishNet/Example/All/SceneManager/Materials/Blue.mat new file mode 100644 index 0000000..cd9821d --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Materials/Blue.mat @@ -0,0 +1,78 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Blue + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: _ALPHAPREMULTIPLY_ON + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: 3000 + stringTagMap: + RenderType: Transparent + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 10 + - _GlossMapScale: 1 + - _Glossiness: 0 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 3 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 0 + m_Colors: + - _Color: {r: 0, g: 0.6867471, b: 1, a: 0.39215687} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/Assets/FishNet/Example/All/SceneManager/Materials/Blue.mat.meta b/Assets/FishNet/Example/All/SceneManager/Materials/Blue.mat.meta new file mode 100644 index 0000000..abdab7d --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Materials/Blue.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d0a99caade0a68842b2274726d1bc7c3 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/SceneManager/Materials/Green.mat b/Assets/FishNet/Example/All/SceneManager/Materials/Green.mat new file mode 100644 index 0000000..04c1cf8 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Materials/Green.mat @@ -0,0 +1,78 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Green + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: _ALPHAPREMULTIPLY_ON + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: 3000 + stringTagMap: + RenderType: Transparent + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 10 + - _GlossMapScale: 1 + - _Glossiness: 0 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 3 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 0 + m_Colors: + - _Color: {r: 0, g: 1, b: 0.030314445, a: 0.39215687} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/Assets/FishNet/Example/All/SceneManager/Materials/Green.mat.meta b/Assets/FishNet/Example/All/SceneManager/Materials/Green.mat.meta new file mode 100644 index 0000000..92fd8a6 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Materials/Green.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e0dd7b8c357813f4ba3ae9b60783a6cd +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/SceneManager/Materials/Red.mat b/Assets/FishNet/Example/All/SceneManager/Materials/Red.mat new file mode 100644 index 0000000..19a839c --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Materials/Red.mat @@ -0,0 +1,78 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Red + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: _ALPHAPREMULTIPLY_ON + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: 3000 + stringTagMap: + RenderType: Transparent + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 10 + - _GlossMapScale: 1 + - _Glossiness: 0 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 3 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 0 + m_Colors: + - _Color: {r: 1, g: 0.018451946, b: 0, a: 0.39215687} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/Assets/FishNet/Example/All/SceneManager/Materials/Red.mat.meta b/Assets/FishNet/Example/All/SceneManager/Materials/Red.mat.meta new file mode 100644 index 0000000..7dd93d4 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Materials/Red.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a35bce0c956282a42a90a04b25492fb6 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/SceneManager/Prefabs.meta b/Assets/FishNet/Example/All/SceneManager/Prefabs.meta new file mode 100644 index 0000000..6dcfebb --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ff677f3c29d59764f917114e0ddf4323 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/SceneManager/Prefabs/Player.prefab b/Assets/FishNet/Example/All/SceneManager/Prefabs/Player.prefab new file mode 100644 index 0000000..bab21db --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Prefabs/Player.prefab @@ -0,0 +1,337 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &1659630665519808908 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2767813079423793104} + - component: {fileID: 2155251397865338026} + - component: {fileID: 6670251115109007609} + m_Layer: 2 + m_Name: Capsule + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2767813079423793104 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1659630665519808908} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.893, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 5090726670223187106} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &2155251397865338026 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1659630665519808908} + m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &6670251115109007609 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1659630665519808908} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!1 &5090726669533971462 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5090726669533971481} + - component: {fileID: 5090726669533971480} + - component: {fileID: 5090726669533971463} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!4 &5090726669533971481 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5090726669533971462} + m_LocalRotation: {x: 0.04100376, y: 0, z: 0, w: 0.99915904} + m_LocalPosition: {x: 0, y: 1.56, z: -4.5} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 5090726670223187106} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 4.7, y: 0, z: 0} +--- !u!20 &5090726669533971480 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5090726669533971462} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!81 &5090726669533971463 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5090726669533971462} + m_Enabled: 1 +--- !u!1 &5090726670223187118 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5090726670223187106} + - component: {fileID: 5090726670223187108} + - component: {fileID: 3514369712614123748} + - component: {fileID: 474764807019926078} + - component: {fileID: 611616139817875448} + - component: {fileID: 6420552185407096997} + - component: {fileID: 6654616088585099699} + m_Layer: 0 + m_Name: Player + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &5090726670223187106 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5090726670223187118} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 5, y: 0, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 5090726669533971481} + - {fileID: 2767813079423793104} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &5090726670223187108 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5090726670223187118} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26e4f626a9ca9704f9befe7673a8dd15, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 611616139817875448} + _networkObjectCache: {fileID: 611616139817875448} + _camera: {fileID: 5090726669533971462} + _moveRate: 8 + _clientAuth: 1 +--- !u!54 &3514369712614123748 +Rigidbody: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5090726670223187118} + serializedVersion: 2 + m_Mass: 1 + m_Drag: 0 + m_AngularDrag: 0.05 + m_UseGravity: 0 + m_IsKinematic: 1 + m_Interpolate: 0 + m_Constraints: 0 + m_CollisionDetection: 0 +--- !u!136 &474764807019926078 +CapsuleCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5090726670223187118} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + m_Radius: 0.5 + m_Height: 1 + m_Direction: 1 + m_Center: {x: 0, y: 0, z: 0} +--- !u!114 &611616139817875448 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5090726670223187118} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + NetworkObserver: {fileID: 0} + k__BackingField: 6 + k__BackingField: 0 + _scenePathHash: 0 + k__BackingField: 0 + k__BackingField: 15644345540858261432 + _sceneNetworkObjects: [] + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: {fileID: 0} + _networkBehaviours: + - {fileID: 5090726670223187108} + - {fileID: 6654616088585099699} + k__BackingField: {fileID: 0} + k__BackingField: [] + _isNetworked: 1 + _isGlobal: 0 + _initializeOrder: 0 + _defaultDespawnType: 0 +--- !u!114 &6420552185407096997 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5090726670223187118} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c71fd7f855ec523429999fc4e14a1928, type: 3} + m_Name: + m_EditorClassIdentifier: + _overrideType: 3 + _updateHostVisibility: 1 + _observerConditions: [] +--- !u!114 &6654616088585099699 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5090726670223187118} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a2836e36774ca1c4bbbee976e17b649c, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 1 + _addedNetworkObject: {fileID: 611616139817875448} + _networkObjectCache: {fileID: 611616139817875448} + _synchronizeParent: 0 + _packing: + Position: 1 + Rotation: 1 + Scale: 0 + _interpolation: 2 + _extrapolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + _clientAuthoritative: 1 + _sendToOwner: 1 + _synchronizePosition: 1 + _positionSnapping: + X: 0 + Y: 0 + Z: 0 + _synchronizeRotation: 1 + _rotationSnapping: + X: 0 + Y: 0 + Z: 0 + _synchronizeScale: 1 + _scaleSnapping: + X: 0 + Y: 0 + Z: 0 diff --git a/Assets/FishNet/Example/All/SceneManager/Prefabs/Player.prefab.meta b/Assets/FishNet/Example/All/SceneManager/Prefabs/Player.prefab.meta new file mode 100644 index 0000000..a7f29b6 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Prefabs/Player.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: bf5f023b4017a5e41a9815ec5745df3d +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/SceneManager/SceneManager Event Diagram.png b/Assets/FishNet/Example/All/SceneManager/SceneManager Event Diagram.png new file mode 100644 index 0000000..de8858b Binary files /dev/null and b/Assets/FishNet/Example/All/SceneManager/SceneManager Event Diagram.png differ diff --git a/Assets/FishNet/Example/All/SceneManager/SceneManager Event Diagram.png.meta b/Assets/FishNet/Example/All/SceneManager/SceneManager Event Diagram.png.meta new file mode 100644 index 0000000..33e28f3 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/SceneManager Event Diagram.png.meta @@ -0,0 +1,92 @@ +fileFormatVersion: 2 +guid: f2231de60d345664cb1831a2e2ebe776 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/SceneManager/Scenes.meta b/Assets/FishNet/Example/All/SceneManager/Scenes.meta new file mode 100644 index 0000000..6d8db28 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cf1de1b0d2f857d41ab48f999ffc7e2c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/SceneManager/Scenes/Additive.meta b/Assets/FishNet/Example/All/SceneManager/Scenes/Additive.meta new file mode 100644 index 0000000..2690e79 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Scenes/Additive.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3d3192e4db2057b46bdf0b1c61bed424 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/SceneManager/Scenes/Additive/AdditiveConnection.unity b/Assets/FishNet/Example/All/SceneManager/Scenes/Additive/AdditiveConnection.unity new file mode 100644 index 0000000..4e63925 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Scenes/Additive/AdditiveConnection.unity @@ -0,0 +1,264 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.37311953, g: 0.38074014, b: 0.3587274, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &76845508 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 76845512} + - component: {fileID: 76845511} + - component: {fileID: 76845510} + - component: {fileID: 76845509} + - component: {fileID: 76845514} + - component: {fileID: 76845513} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &76845509 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 76845508} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &76845510 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 76845508} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &76845511 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 76845508} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &76845512 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 76845508} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -5, y: 0, z: 10} + m_LocalScale: {x: 4, y: 4, z: 4} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &76845513 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 76845508} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + NetworkObserver: {fileID: 0} + k__BackingField: -1 + _scenePathHash: 3284346746 + k__BackingField: 14106161864781660253 + _sceneNetworkObjects: + - {fileID: 76845513} + k__BackingField: 0 + k__BackingField: 0 + _networkBehaviours: + - {fileID: 76845514} + k__BackingField: {fileID: 0} + k__BackingField: [] + _isNetworked: 1 + _isGlobal: 0 + _disableOnDespawn: 0 +--- !u!114 &76845514 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 76845508} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c71fd7f855ec523429999fc4e14a1928, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 76845513} + _networkObjectCache: {fileID: 76845513} + _overrideType: 3 + _setHostVisibility: 1 + _observerConditions: + - {fileID: 11400000, guid: 2033f54fd2794464bae08fa5a55c8996, type: 2} diff --git a/Assets/FishNet/Example/All/SceneManager/Scenes/Additive/AdditiveConnection.unity.meta b/Assets/FishNet/Example/All/SceneManager/Scenes/Additive/AdditiveConnection.unity.meta new file mode 100644 index 0000000..807c4f4 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Scenes/Additive/AdditiveConnection.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: cc489be8bb6ae444283f394c5e5fa8e2 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/SceneManager/Scenes/Additive/AdditiveGlobal.unity b/Assets/FishNet/Example/All/SceneManager/Scenes/Additive/AdditiveGlobal.unity new file mode 100644 index 0000000..85d0f6f --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Scenes/Additive/AdditiveGlobal.unity @@ -0,0 +1,264 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.37311953, g: 0.38074014, b: 0.3587274, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &1950515027 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1950515031} + - component: {fileID: 1950515030} + - component: {fileID: 1950515029} + - component: {fileID: 1950515028} + - component: {fileID: 1950515033} + - component: {fileID: 1950515032} + m_Layer: 0 + m_Name: Sphere + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!135 &1950515028 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1950515027} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1950515029 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1950515027} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &1950515030 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1950515027} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1950515031 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1950515027} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 5, y: 0, z: 10} + m_LocalScale: {x: 4, y: 4, z: 4} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1950515032 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1950515027} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + NetworkObserver: {fileID: 0} + k__BackingField: -1 + _scenePathHash: 2384367787 + k__BackingField: 10240781668764572782 + _sceneNetworkObjects: + - {fileID: 1950515032} + k__BackingField: 0 + k__BackingField: 0 + _networkBehaviours: + - {fileID: 1950515033} + k__BackingField: {fileID: 0} + k__BackingField: [] + _isNetworked: 1 + _isGlobal: 0 + _disableOnDespawn: 0 +--- !u!114 &1950515033 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1950515027} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c71fd7f855ec523429999fc4e14a1928, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 1950515032} + _networkObjectCache: {fileID: 1950515032} + _overrideType: 3 + _setHostVisibility: 1 + _observerConditions: + - {fileID: 11400000, guid: 2033f54fd2794464bae08fa5a55c8996, type: 2} diff --git a/Assets/FishNet/Example/All/SceneManager/Scenes/Additive/AdditiveGlobal.unity.meta b/Assets/FishNet/Example/All/SceneManager/Scenes/Additive/AdditiveGlobal.unity.meta new file mode 100644 index 0000000..36036b2 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Scenes/Additive/AdditiveGlobal.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c43835f124dc68747a2091c4b8c42f80 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/SceneManager/Scenes/Additive/AdditiveMain.unity b/Assets/FishNet/Example/All/SceneManager/Scenes/Additive/AdditiveMain.unity new file mode 100644 index 0000000..dc78e62 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Scenes/Additive/AdditiveMain.unity @@ -0,0 +1,1265 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 705507994} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 500 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 2 + m_PVRDenoiserTypeDirect: 0 + m_PVRDenoiserTypeIndirect: 0 + m_PVRDenoiserTypeAO: 0 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 0 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &705507993 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 705507995} + - component: {fileID: 705507994} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &705507994 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 705507993} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 1 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &705507995 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 705507993} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: -2.2843354, y: 60.72741, z: -2.9938574} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1155569020} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &825925881 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 825925885} + - component: {fileID: 825925886} + - component: {fileID: 825925887} + - component: {fileID: 825925884} + - component: {fileID: 825925883} + - component: {fileID: 825925882} + m_Layer: 0 + m_Name: Connection + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!135 &825925882 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 825925881} + m_Material: {fileID: 0} + m_IsTrigger: 1 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &825925883 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 825925881} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: d0a99caade0a68842b2274726d1bc7c3, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &825925884 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 825925881} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &825925885 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 825925881} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -4.7799997, y: -3, z: 10} + m_LocalScale: {x: 15, y: 15, z: 15} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &825925886 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 825925881} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fa23b6e6f9b08d74885e3707aa0d9bc7, type: 3} + m_Name: + m_EditorClassIdentifier: + _moveObject: 0 + _moveAllObjects: 0 + _replaceOption: 2 + _scenes: + - AdditiveConnection + _connectionOnly: 1 + _automaticallyUnload: 1 + _onTriggerEnter: 1 +--- !u!114 &825925887 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 825925881} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 84ae572fef1171b41ab287d1c9b5da63, type: 3} + m_Name: + m_EditorClassIdentifier: + _scenes: + - AdditiveConnection + _connectionOnly: 1 + _unloadUnused: 1 + _onTriggerEnter: 0 +--- !u!1 &1155569019 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1155569020} + m_Layer: 0 + m_Name: Scene + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1155569020 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1155569019} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 705507995} + - {fileID: 2114768053} + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1406093434 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1406093440} + - component: {fileID: 1406093439} + - component: {fileID: 1406093438} + - component: {fileID: 1406093437} + - component: {fileID: 1406093436} + - component: {fileID: 1406093435} + m_Layer: 0 + m_Name: Global + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!135 &1406093435 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1406093434} + m_Material: {fileID: 0} + m_IsTrigger: 1 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1406093436 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1406093434} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: a35bce0c956282a42a90a04b25492fb6, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &1406093437 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1406093434} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!114 &1406093438 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1406093434} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 84ae572fef1171b41ab287d1c9b5da63, type: 3} + m_Name: + m_EditorClassIdentifier: + _scenes: + - AdditiveGlobal + _connectionOnly: 0 + _unloadUnused: 1 + _onTriggerEnter: 0 +--- !u!114 &1406093439 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1406093434} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fa23b6e6f9b08d74885e3707aa0d9bc7, type: 3} + m_Name: + m_EditorClassIdentifier: + _moveObject: 0 + _moveAllObjects: 0 + _replaceOption: 2 + _scenes: + - AdditiveGlobal + _connectionOnly: 0 + _automaticallyUnload: 1 + _onTriggerEnter: 1 +--- !u!4 &1406093440 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1406093434} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 4.89, y: -3, z: 10} + m_LocalScale: {x: 15, y: 15, z: 15} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &2114768049 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2114768053} + - component: {fileID: 2114768052} + - component: {fileID: 2114768051} + - component: {fileID: 2114768050} + m_Layer: 0 + m_Name: Plane + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!64 &2114768050 +MeshCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2114768049} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 4 + m_Convex: 0 + m_CookingOptions: 30 + m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &2114768051 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2114768049} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &2114768052 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2114768049} + m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &2114768053 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2114768049} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: -5, z: 0} + m_LocalScale: {x: 10, y: 10, z: 10} + m_Children: [] + m_Father: {fileID: 1155569020} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &174578014943721374 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6832184529280211084} + - component: {fileID: 9050426448999039876} + - component: {fileID: 3730311944083398519} + m_Layer: 5 + m_Name: Indicator + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1661284633666962301 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7114397409874979456} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.25490198, g: 0.25490198, b: 0.25490198, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 2b3dca501a9d8c8479dc71dd068aa8b8, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &2085292595826649312 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2085292595826649325} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 2d50394614f8feb4eb0567fb7618d84d, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &2085292595826649313 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2085292595826649325} + m_CullTransparentMesh: 0 +--- !u!1 &2085292595826649325 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2085292595826649326} + - component: {fileID: 2085292595826649313} + - component: {fileID: 2085292595826649312} + - component: {fileID: 2085292595826649327} + m_Layer: 5 + m_Name: Client + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2085292595826649326 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2085292595826649325} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 6832184529280211084} + m_Father: {fileID: 2085292596359202251} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 16, y: -96} + m_SizeDelta: {x: 256, y: 64} + m_Pivot: {x: 0, y: 1} +--- !u!114 &2085292595826649327 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2085292595826649325} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 2085292595826649312} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 2085292596359202263} + m_MethodName: OnClick_Client + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!114 &2085292596010868064 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2085292596010868077} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 1b187e63031bf7849b249c8212440c3b, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &2085292596010868065 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2085292596010868077} + m_CullTransparentMesh: 0 +--- !u!1 &2085292596010868077 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2085292596010868078} + - component: {fileID: 2085292596010868065} + - component: {fileID: 2085292596010868064} + - component: {fileID: 2085292596010868079} + m_Layer: 5 + m_Name: Server + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2085292596010868078 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2085292596010868077} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4928688179816641390} + m_Father: {fileID: 2085292596359202251} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 16, y: -16} + m_SizeDelta: {x: 256, y: 64} + m_Pivot: {x: 0, y: 1} +--- !u!114 &2085292596010868079 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2085292596010868077} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 2085292596010868064} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 2085292596359202263} + m_MethodName: OnClick_Server + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!114 &2085292596359202248 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2085292596359202262} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &2085292596359202249 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2085292596359202262} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 1 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 1920, y: 1080} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0.5 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 +--- !u!223 &2085292596359202250 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2085292596359202262} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &2085292596359202251 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2085292596359202262} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_Children: + - {fileID: 2085292596010868078} + - {fileID: 2085292595826649326} + m_Father: {fileID: 7443408887680269496} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!1 &2085292596359202262 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2085292596359202251} + - component: {fileID: 2085292596359202250} + - component: {fileID: 2085292596359202249} + - component: {fileID: 2085292596359202248} + - component: {fileID: 2085292596359202263} + m_Layer: 5 + m_Name: NetworkHudCanvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &2085292596359202263 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2085292596359202262} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6d3606bfdac5a4743890fc1a5ecd8f24, type: 3} + m_Name: + m_EditorClassIdentifier: + _autoStartType: 0 + _stoppedColor: {r: 0.25490198, g: 0.25490198, b: 0.25490198, a: 1} + _changingColor: {r: 0.78431374, g: 0.6862745, b: 0, a: 1} + _startedColor: {r: 0, g: 0.5882353, b: 0.64705884, a: 1} + _serverIndicator: {fileID: 1661284633666962301} + _clientIndicator: {fileID: 3730311944083398519} +--- !u!114 &3730311944083398519 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 174578014943721374} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.25490198, g: 0.25490198, b: 0.25490198, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 2b3dca501a9d8c8479dc71dd068aa8b8, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!224 &4928688179816641390 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7114397409874979456} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -1} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 2085292596010868078} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!224 &6832184529280211084 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 174578014943721374} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -1} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 2085292595826649326} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &7114397409874979456 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4928688179816641390} + - component: {fileID: 7408958669887450887} + - component: {fileID: 1661284633666962301} + m_Layer: 5 + m_Name: Indicator + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!222 &7408958669887450887 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7114397409874979456} + m_CullTransparentMesh: 0 +--- !u!4 &7443408887680269496 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408887680269498} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -2.2843354, y: 57.72741, z: -2.9938574} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 2085292596359202251} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &7443408887680269498 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7443408887680269496} + - component: {fileID: 7443408887680269499} + - component: {fileID: 7443408887680269500} + - component: {fileID: 7443408887680269501} + - component: {fileID: 7443408887680269502} + - component: {fileID: 7443408887680269503} + m_Layer: 0 + m_Name: 'NetworkManager ' + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &7443408887680269499 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408887680269498} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d2c95dfde7d73b54dbbdc23155d35d36, type: 3} + m_Name: + m_EditorClassIdentifier: + _logging: {fileID: 0} + _spawnablePrefabs: {fileID: 11400000, guid: 3a54436bdb916194f99da0d17231e617, type: 2} + _refreshDefaultPrefabs: 1 + _runInBackground: 1 + _dontDestroyOnLoad: 1 + _objectPool: {fileID: 0} + _persistence: 0 +--- !u!114 &7443408887680269500 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408887680269498} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3fdaae44044276a49a52229c1597e33b, type: 3} + m_Name: + m_EditorClassIdentifier: + _updateOrder: 0 + _timingType: 0 + _allowTickDropping: 0 + _maximumFrameTicks: 2 + _tickRate: 30 + _pingInterval: 1 + _timingInterval: 2 + _physicsMode: 0 +--- !u!114 &7443408887680269501 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408887680269498} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6f48f002b825cbd45a19bd96d90f9edb, type: 3} + m_Name: + m_EditorClassIdentifier: + _unreliableMTU: 1023 + _ipv4BindAddress: + _ipv6BindAddress: + _port: 7770 + _maximumClients: 4095 + _clientAddress: localhost + _timeout: 15 +--- !u!114 &7443408887680269502 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408887680269498} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 68828c85278210948b9d50a8db3aab74, type: 3} + m_Name: + m_EditorClassIdentifier: + _authenticator: {fileID: 0} + _syncTypeRate: 0.1 + SpawnPacking: + Position: 0 + Rotation: 2 + Scale: 2 + _changeFrameRate: 1 + _frameRate: 9999 + _shareIds: 1 + _startOnHeadless: 1 + _limitClientMTU: 1 +--- !u!114 &7443408887680269503 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408887680269498} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 211a9f6ec51ddc14f908f5acc0cd0423, type: 3} + m_Name: + m_EditorClassIdentifier: + _playerPrefab: {fileID: 611616139817875448, guid: bf5f023b4017a5e41a9815ec5745df3d, + type: 3} + _addToDefaultScene: 1 + Spawns: [] +--- !u!222 &9050426448999039876 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 174578014943721374} + m_CullTransparentMesh: 0 diff --git a/Assets/FishNet/Example/All/SceneManager/Scenes/Additive/AdditiveMain.unity.meta b/Assets/FishNet/Example/All/SceneManager/Scenes/Additive/AdditiveMain.unity.meta new file mode 100644 index 0000000..86dfccb --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Scenes/Additive/AdditiveMain.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e7d3ac2d556912042aca9aa1947aea07 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/SceneManager/Scenes/Replace.meta b/Assets/FishNet/Example/All/SceneManager/Scenes/Replace.meta new file mode 100644 index 0000000..7b51256 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Scenes/Replace.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2ad819293f3cbbf44b9e2790853833c5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/SceneManager/Scenes/Replace/ReplaceConnection.unity b/Assets/FishNet/Example/All/SceneManager/Scenes/Replace/ReplaceConnection.unity new file mode 100644 index 0000000..836be96 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Scenes/Replace/ReplaceConnection.unity @@ -0,0 +1,624 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &76845508 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 76845512} + - component: {fileID: 76845511} + - component: {fileID: 76845510} + - component: {fileID: 76845509} + - component: {fileID: 76845514} + - component: {fileID: 76845513} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &76845509 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 76845508} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &76845510 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 76845508} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &76845511 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 76845508} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &76845512 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 76845508} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -19.2, y: 3.8, z: -0.43} + m_LocalScale: {x: 4, y: 4, z: 4} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &76845513 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 76845508} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + NetworkObserver: {fileID: 0} + k__BackingField: -1 + _scenePathHash: 3254454948 + k__BackingField: 13977777569408466860 + k__BackingField: 0 + _sceneNetworkObjects: + - {fileID: 76845513} + k__BackingField: 0 + k__BackingField: 0 + _networkBehaviours: [] + k__BackingField: {fileID: 0} + k__BackingField: [] + _isNetworked: 1 + _isGlobal: 0 + _defaultDespawnType: 0 +--- !u!114 &76845514 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 76845508} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c71fd7f855ec523429999fc4e14a1928, type: 3} + m_Name: + m_EditorClassIdentifier: + _overrideType: 3 + _updateHostVisibility: 1 + _observerConditions: + - {fileID: 11400000, guid: 2033f54fd2794464bae08fa5a55c8996, type: 2} +--- !u!1 &603255273 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 603255274} + m_Layer: 0 + m_Name: Triggers + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &603255274 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 603255273} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -22.68, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 979031081727779170} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &748707377276284900 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 748707377276284902} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: -2.2843354, y: 60.72741, z: -2.9938574} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 748707378658658307} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!108 &748707377276284901 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 748707377276284902} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 1 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!1 &748707377276284902 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 748707377276284900} + - component: {fileID: 748707377276284901} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &748707378551427530 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 748707378551427534} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: -5, z: 0} + m_LocalScale: {x: 10, y: 10, z: 10} + m_Children: [] + m_Father: {fileID: 748707378658658307} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &748707378551427531 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 748707378551427534} + m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &748707378551427532 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 748707378551427534} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!64 &748707378551427533 +MeshCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 748707378551427534} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 4 + m_Convex: 0 + m_CookingOptions: 30 + m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &748707378551427534 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 748707378551427530} + - component: {fileID: 748707378551427531} + - component: {fileID: 748707378551427532} + - component: {fileID: 748707378551427533} + m_Layer: 0 + m_Name: Plane + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &748707378658658307 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 748707378658658308} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 748707377276284900} + - {fileID: 748707378551427530} + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &748707378658658308 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 748707378658658307} + m_Layer: 0 + m_Name: Scene + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &979031081727779169 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 979031081727779174} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fa23b6e6f9b08d74885e3707aa0d9bc7, type: 3} + m_Name: + m_EditorClassIdentifier: + _moveObject: 1 + _moveAllObjects: 0 + _replaceOption: 0 + _scenes: + - ReplaceMain + _connectionOnly: 1 + _automaticallyUnload: 1 + _onTriggerEnter: 1 +--- !u!4 &979031081727779170 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 979031081727779174} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 2.87, y: -3, z: 0} + m_LocalScale: {x: 15, y: 15, z: 15} + m_Children: [] + m_Father: {fileID: 603255274} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &979031081727779171 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 979031081727779174} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &979031081727779172 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 979031081727779174} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 2e7517d1496ae784f94a2307a88e2bb5, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!135 &979031081727779173 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 979031081727779174} + m_Material: {fileID: 0} + m_IsTrigger: 1 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!1 &979031081727779174 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 979031081727779170} + - component: {fileID: 979031081727779169} + - component: {fileID: 979031081727779171} + - component: {fileID: 979031081727779172} + - component: {fileID: 979031081727779173} + m_Layer: 0 + m_Name: Connection + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 diff --git a/Assets/FishNet/Example/All/SceneManager/Scenes/Replace/ReplaceConnection.unity.meta b/Assets/FishNet/Example/All/SceneManager/Scenes/Replace/ReplaceConnection.unity.meta new file mode 100644 index 0000000..0eb3735 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Scenes/Replace/ReplaceConnection.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 58053e81b62de3a499afaf0f73d01b01 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/SceneManager/Scenes/Replace/ReplaceGlobal.unity b/Assets/FishNet/Example/All/SceneManager/Scenes/Replace/ReplaceGlobal.unity new file mode 100644 index 0000000..4181a2f --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Scenes/Replace/ReplaceGlobal.unity @@ -0,0 +1,724 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &1808099118 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1808099119} + - component: {fileID: 1808099124} + - component: {fileID: 1808099123} + - component: {fileID: 1808099122} + - component: {fileID: 1808099120} + m_Layer: 0 + m_Name: SphereChild + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1808099119 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1808099118} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: -0.637, z: 0} + m_LocalScale: {x: 0.5, y: 0.5, z: 0.5} + m_Children: [] + m_Father: {fileID: 1950515031} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1808099120 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1808099118} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + NetworkObserver: {fileID: 0} + k__BackingField: 65535 + k__BackingField: 0 + _scenePathHash: 3074121493 + k__BackingField: 13203251279664725889 + k__BackingField: 0 + _sceneNetworkObjects: + - {fileID: 1808099120} + - {fileID: 1950515032} + k__BackingField: 1 + k__BackingField: 1 + k__BackingField: {fileID: 0} + _networkBehaviours: [] + k__BackingField: {fileID: 1950515032} + k__BackingField: [] + _isNetworked: 1 + _isGlobal: 0 + _initializeOrder: 0 + _defaultDespawnType: 0 +--- !u!135 &1808099122 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1808099118} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1808099123 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1808099118} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &1808099124 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1808099118} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1950515027 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1950515031} + - component: {fileID: 1950515030} + - component: {fileID: 1950515029} + - component: {fileID: 1950515028} + - component: {fileID: 1950515033} + - component: {fileID: 1950515032} + m_Layer: 0 + m_Name: Sphere + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!135 &1950515028 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1950515027} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1950515029 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1950515027} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &1950515030 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1950515027} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1950515031 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1950515027} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 24.31, y: 3, z: -5.47} + m_LocalScale: {x: 4, y: 4, z: 4} + m_Children: + - {fileID: 1808099119} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1950515032 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1950515027} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + NetworkObserver: {fileID: 0} + k__BackingField: 65535 + k__BackingField: 0 + _scenePathHash: 3074121493 + k__BackingField: 13203251279314293806 + k__BackingField: 0 + _sceneNetworkObjects: + - {fileID: 1808099120} + - {fileID: 1950515032} + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: {fileID: 0} + _networkBehaviours: [] + k__BackingField: {fileID: 0} + k__BackingField: + - {fileID: 1808099120} + _isNetworked: 1 + _isGlobal: 0 + _initializeOrder: 0 + _defaultDespawnType: 0 +--- !u!114 &1950515033 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1950515027} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c71fd7f855ec523429999fc4e14a1928, type: 3} + m_Name: + m_EditorClassIdentifier: + _overrideType: 3 + _updateHostVisibility: 1 + _observerConditions: + - {fileID: 11400000, guid: 2033f54fd2794464bae08fa5a55c8996, type: 2} +--- !u!1 &1722183419489630397 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1722183419489630399} + - component: {fileID: 1722183419489630398} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &1722183419489630398 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1722183419489630397} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 1 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &1722183419489630399 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1722183419489630397} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: -2.2843354, y: 60.72741, z: -2.9938574} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1722183420925353816} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!33 &1722183420764669584 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1722183420764669589} + m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1722183420764669585 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1722183420764669589} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: -5, z: 0} + m_LocalScale: {x: 10, y: 10, z: 10} + m_Children: [] + m_Father: {fileID: 1722183420925353816} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1722183420764669589 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1722183420764669585} + - component: {fileID: 1722183420764669584} + - component: {fileID: 1722183420764669591} + - component: {fileID: 1722183420764669590} + m_Layer: 0 + m_Name: Plane + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!64 &1722183420764669590 +MeshCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1722183420764669589} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 4 + m_Convex: 0 + m_CookingOptions: 30 + m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &1722183420764669591 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1722183420764669589} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!4 &1722183420925353816 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1722183420925353823} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1722183419489630399} + - {fileID: 1722183420764669585} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1722183420925353823 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1722183420925353816} + m_Layer: 0 + m_Name: Scene + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &5581392219061976576 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5581392219061976826} + - component: {fileID: 5581392219061976581} + - component: {fileID: 5581392219061976583} + - component: {fileID: 5581392219061976582} + - component: {fileID: 5581392219061976577} + m_Layer: 0 + m_Name: Global + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!135 &5581392219061976577 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5581392219061976576} + m_Material: {fileID: 0} + m_IsTrigger: 1 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!114 &5581392219061976581 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5581392219061976576} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fa23b6e6f9b08d74885e3707aa0d9bc7, type: 3} + m_Name: + m_EditorClassIdentifier: + _moveObject: 0 + _moveAllObjects: 1 + _replaceOption: 0 + _scenes: + - ReplaceMain + _connectionOnly: 0 + _automaticallyUnload: 1 + _onTriggerEnter: 1 +--- !u!23 &5581392219061976582 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5581392219061976576} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 2e7517d1496ae784f94a2307a88e2bb5, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &5581392219061976583 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5581392219061976576} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &5581392219061976826 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5581392219061976576} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 30.66, y: -3, z: -1.24} + m_LocalScale: {x: 15, y: 15, z: 15} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Assets/FishNet/Example/All/SceneManager/Scenes/Replace/ReplaceGlobal.unity.meta b/Assets/FishNet/Example/All/SceneManager/Scenes/Replace/ReplaceGlobal.unity.meta new file mode 100644 index 0000000..58f62b6 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Scenes/Replace/ReplaceGlobal.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: fddd81e62368fe9448b0eb0a80da6bb4 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/SceneManager/Scenes/Replace/ReplaceMain.unity b/Assets/FishNet/Example/All/SceneManager/Scenes/Replace/ReplaceMain.unity new file mode 100644 index 0000000..08ea094 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Scenes/Replace/ReplaceMain.unity @@ -0,0 +1,1271 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 705507994} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 500 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 2 + m_PVRDenoiserTypeDirect: 0 + m_PVRDenoiserTypeIndirect: 0 + m_PVRDenoiserTypeAO: 0 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 0 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &40691391 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 40691392} + m_Layer: 0 + m_Name: Triggers + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &40691392 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 40691391} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 825925885} + - {fileID: 1406093440} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &705507993 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 705507995} + - component: {fileID: 705507994} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &705507994 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 705507993} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 1 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &705507995 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 705507993} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: -2.2843354, y: 60.72741, z: -2.9938574} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1155569020} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &825925881 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 825925885} + - component: {fileID: 825925886} + - component: {fileID: 825925884} + - component: {fileID: 825925883} + - component: {fileID: 825925882} + m_Layer: 0 + m_Name: Connection + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!135 &825925882 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 825925881} + m_Material: {fileID: 0} + m_IsTrigger: 1 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &825925883 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 825925881} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: d0a99caade0a68842b2274726d1bc7c3, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &825925884 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 825925881} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &825925885 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 825925881} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -2.4611313, y: -3, z: 2.2759757} + m_LocalScale: {x: 15, y: 15, z: 15} + m_Children: [] + m_Father: {fileID: 40691392} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &825925886 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 825925881} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fa23b6e6f9b08d74885e3707aa0d9bc7, type: 3} + m_Name: + m_EditorClassIdentifier: + _moveObject: 1 + _moveAllObjects: 0 + _replaceOption: 0 + _scenes: + - ReplaceConnection + _connectionOnly: 1 + _automaticallyUnload: 1 + _onTriggerEnter: 1 +--- !u!1 &1155569019 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1155569020} + m_Layer: 0 + m_Name: Scene + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1155569020 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1155569019} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 705507995} + - {fileID: 2114768053} + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1406093434 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1406093440} + - component: {fileID: 1406093439} + - component: {fileID: 1406093437} + - component: {fileID: 1406093436} + - component: {fileID: 1406093435} + m_Layer: 0 + m_Name: Global + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!135 &1406093435 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1406093434} + m_Material: {fileID: 0} + m_IsTrigger: 1 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1406093436 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1406093434} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: a35bce0c956282a42a90a04b25492fb6, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &1406093437 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1406093434} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!114 &1406093439 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1406093434} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fa23b6e6f9b08d74885e3707aa0d9bc7, type: 3} + m_Name: + m_EditorClassIdentifier: + _moveObject: 0 + _moveAllObjects: 1 + _replaceOption: 1 + _scenes: + - ReplaceGlobal + _connectionOnly: 0 + _automaticallyUnload: 1 + _onTriggerEnter: 1 +--- !u!4 &1406093440 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1406093434} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 13.88, y: -3, z: 2.2759757} + m_LocalScale: {x: 15, y: 15, z: 15} + m_Children: [] + m_Father: {fileID: 40691392} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &2114768049 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2114768053} + - component: {fileID: 2114768052} + - component: {fileID: 2114768051} + - component: {fileID: 2114768050} + m_Layer: 0 + m_Name: Plane + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!64 &2114768050 +MeshCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2114768049} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 4 + m_Convex: 0 + m_CookingOptions: 30 + m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &2114768051 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2114768049} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &2114768052 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2114768049} + m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &2114768053 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2114768049} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: -5, z: 0} + m_LocalScale: {x: 10, y: 10, z: 10} + m_Children: [] + m_Father: {fileID: 1155569020} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1357903939251608625 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2553749094314996952} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.25490198, g: 0.25490198, b: 0.25490198, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 2b3dca501a9d8c8479dc71dd068aa8b8, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!1 &2553749094314996952 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 9208822351047973834} + - component: {fileID: 6666759449596494018} + - component: {fileID: 1357903939251608625} + m_Layer: 5 + m_Name: Indicator + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &3896340524768297019 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4875127092859136454} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.25490198, g: 0.25490198, b: 0.25490198, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 2b3dca501a9d8c8479dc71dd068aa8b8, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &4462211894441225126 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4462211894441225131} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 2d50394614f8feb4eb0567fb7618d84d, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &4462211894441225127 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4462211894441225131} + m_CullTransparentMesh: 0 +--- !u!224 &4462211894441225128 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4462211894441225131} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 9208822351047973834} + m_Father: {fileID: 4462211894982264461} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 16, y: -96} + m_SizeDelta: {x: 256, y: 64} + m_Pivot: {x: 0, y: 1} +--- !u!114 &4462211894441225129 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4462211894441225131} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 4462211894441225126} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 4462211894982264465} + m_MethodName: OnClick_Client + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!1 &4462211894441225131 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4462211894441225128} + - component: {fileID: 4462211894441225127} + - component: {fileID: 4462211894441225126} + - component: {fileID: 4462211894441225129} + m_Layer: 5 + m_Name: Client + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &4462211894961511974 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4462211894961511979} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 1b187e63031bf7849b249c8212440c3b, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &4462211894961511975 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4462211894961511979} + m_CullTransparentMesh: 0 +--- !u!224 &4462211894961511976 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4462211894961511979} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 7312354082057744424} + m_Father: {fileID: 4462211894982264461} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 16, y: -16} + m_SizeDelta: {x: 256, y: 64} + m_Pivot: {x: 0, y: 1} +--- !u!114 &4462211894961511977 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4462211894961511979} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 4462211894961511974} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 4462211894982264465} + m_MethodName: OnClick_Server + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!1 &4462211894961511979 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4462211894961511976} + - component: {fileID: 4462211894961511975} + - component: {fileID: 4462211894961511974} + - component: {fileID: 4462211894961511977} + m_Layer: 5 + m_Name: Server + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!223 &4462211894982264460 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4462211894982264464} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &4462211894982264461 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4462211894982264464} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_Children: + - {fileID: 4462211894961511976} + - {fileID: 4462211894441225128} + m_Father: {fileID: 7443408887680269496} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!114 &4462211894982264462 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4462211894982264464} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &4462211894982264463 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4462211894982264464} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 1 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 1920, y: 1080} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0.5 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 +--- !u!1 &4462211894982264464 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4462211894982264461} + - component: {fileID: 4462211894982264460} + - component: {fileID: 4462211894982264465} + - component: {fileID: 4462211894982264463} + - component: {fileID: 4462211894982264462} + m_Layer: 5 + m_Name: NetworkHudCanvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &4462211894982264465 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4462211894982264464} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6d3606bfdac5a4743890fc1a5ecd8f24, type: 3} + m_Name: + m_EditorClassIdentifier: + _autoStartType: 2 + _stoppedColor: {r: 0.25490198, g: 0.25490198, b: 0.25490198, a: 1} + _changingColor: {r: 0.78431374, g: 0.6862745, b: 0, a: 1} + _startedColor: {r: 0, g: 0.5882353, b: 0.64705884, a: 1} + _serverIndicator: {fileID: 3896340524768297019} + _clientIndicator: {fileID: 1357903939251608625} +--- !u!1 &4875127092859136454 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7312354082057744424} + - component: {fileID: 5173910453633412161} + - component: {fileID: 3896340524768297019} + m_Layer: 5 + m_Name: Indicator + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!222 &5173910453633412161 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4875127092859136454} + m_CullTransparentMesh: 0 +--- !u!222 &6666759449596494018 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2553749094314996952} + m_CullTransparentMesh: 0 +--- !u!224 &7312354082057744424 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4875127092859136454} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -1} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4462211894961511976} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &7443408887680269493 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408887680269498} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 211a9f6ec51ddc14f908f5acc0cd0423, type: 3} + m_Name: + m_EditorClassIdentifier: + _playerPrefab: {fileID: 611616139817875448, guid: bf5f023b4017a5e41a9815ec5745df3d, + type: 3} + _addToDefaultScene: 0 + Spawns: [] +--- !u!4 &7443408887680269496 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408887680269498} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4462211894982264461} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &7443408887680269498 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7443408887680269496} + - component: {fileID: 7443408887680269499} + - component: {fileID: 7443408887680269493} + - component: {fileID: 7443408887680269500} + - component: {fileID: 7443408887680269501} + - component: {fileID: 7443408887680269502} + - component: {fileID: 7443408887680269503} + m_Layer: 0 + m_Name: 'NetworkManager ' + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &7443408887680269499 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408887680269498} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d2c95dfde7d73b54dbbdc23155d35d36, type: 3} + m_Name: + m_EditorClassIdentifier: + _logging: {fileID: 0} + _spawnablePrefabs: {fileID: 11400000, guid: 3a54436bdb916194f99da0d17231e617, type: 2} + _refreshDefaultPrefabs: 1 + _runInBackground: 1 + _dontDestroyOnLoad: 1 + _objectPool: {fileID: 0} + _persistence: 0 +--- !u!114 &7443408887680269500 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408887680269498} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3fdaae44044276a49a52229c1597e33b, type: 3} + m_Name: + m_EditorClassIdentifier: + _updateOrder: 0 + _timingType: 0 + _allowTickDropping: 0 + _maximumFrameTicks: 2 + _tickRate: 30 + _pingInterval: 1 + _timingInterval: 2 + _physicsMode: 0 +--- !u!114 &7443408887680269501 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408887680269498} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6f48f002b825cbd45a19bd96d90f9edb, type: 3} + m_Name: + m_EditorClassIdentifier: + _unreliableMTU: 1023 + _ipv4BindAddress: + _ipv6BindAddress: + _port: 7770 + _maximumClients: 4095 + _clientAddress: localhost + _timeout: 15 +--- !u!114 &7443408887680269502 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408887680269498} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 15895a51081447d46bda466e7e830c08, type: 3} + m_Name: + m_EditorClassIdentifier: + _sceneProcessor: {fileID: 0} + _lightProbeUpdating: 0 + _moveClientHostObjects: 1 + _setActiveScene: 1 +--- !u!114 &7443408887680269503 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408887680269498} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7d331f979d46e8e4a9fc90070c596d44, type: 3} + m_Name: + m_EditorClassIdentifier: + _useNetworkLod: 0 + _levelOfDetailDistances: [] + _updateHostVisibility: 1 + _defaultConditions: [] +--- !u!224 &9208822351047973834 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2553749094314996952} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -1} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4462211894441225128} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} diff --git a/Assets/FishNet/Example/All/SceneManager/Scenes/Replace/ReplaceMain.unity.meta b/Assets/FishNet/Example/All/SceneManager/Scenes/Replace/ReplaceMain.unity.meta new file mode 100644 index 0000000..5276e6e --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Scenes/Replace/ReplaceMain.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 099398b014b86004abd99a7d21cf417a +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/SceneManager/Scripts.meta b/Assets/FishNet/Example/All/SceneManager/Scripts.meta new file mode 100644 index 0000000..83ffc63 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 237bdcd1cdb63df4089e95bd92e1c69e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/SceneManager/Scripts/PlayerController.cs b/Assets/FishNet/Example/All/SceneManager/Scripts/PlayerController.cs new file mode 100644 index 0000000..81d8594 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Scripts/PlayerController.cs @@ -0,0 +1,75 @@ +using FishNet.Connection; +using FishNet.Object; +using UnityEngine; + +namespace FishNet.Example.Scened +{ + + + public class PlayerController : NetworkBehaviour + { + [SerializeField] + private GameObject _camera; + [SerializeField] + private float _moveRate = 4f; + [SerializeField] + private bool _clientAuth = true; + + public override void OnStartClient() + { + base.OnStartClient(); + if (base.IsOwner) + _camera.SetActive(true); + } + + private void Update() + { + if (!base.IsOwner) + return; + + float hor = Input.GetAxisRaw("Horizontal"); + float ver = Input.GetAxisRaw("Vertical"); + + /* If ground cannot be found for 20 units then bump up 3 units. + * This is just to keep player on ground if they fall through + * when changing scenes. */ + if (_clientAuth || (!_clientAuth && base.IsServer)) + { + if (!Physics.Linecast(transform.position + new Vector3(0f, 0.3f, 0f), transform.position - (Vector3.one * 20f))) + transform.position += new Vector3(0f, 3f, 0f); + } + + if (_clientAuth) + Move(hor, ver); + else + ServerMove(hor, ver); + } + + [ServerRpc] + private void ServerMove(float hor, float ver) + { + Move(hor, ver); + } + + private void Move(float hor, float ver) + { + float gravity = -10f * Time.deltaTime; + //If ray hits floor then cancel gravity. + Ray ray = new Ray(transform.position + new Vector3(0f, 0.05f, 0f), -Vector3.up); + if (Physics.Raycast(ray, 0.1f + -gravity)) + gravity = 0f; + + /* Moving. */ + Vector3 direction = new Vector3( + 0f, + gravity, + ver * _moveRate * Time.deltaTime); + + transform.position += transform.TransformDirection(direction); + transform.Rotate(new Vector3(0f, hor * 100f * Time.deltaTime, 0f)); + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Example/All/SceneManager/Scripts/PlayerController.cs.meta b/Assets/FishNet/Example/All/SceneManager/Scripts/PlayerController.cs.meta new file mode 100644 index 0000000..cbdc08d --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Scripts/PlayerController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 26e4f626a9ca9704f9befe7673a8dd15 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/SceneManager/Scripts/SceneLoaderExample.cs b/Assets/FishNet/Example/All/SceneManager/Scripts/SceneLoaderExample.cs new file mode 100644 index 0000000..9569eef --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Scripts/SceneLoaderExample.cs @@ -0,0 +1,148 @@ +using FishNet.Connection; +using FishNet.Managing.Logging; +using FishNet.Managing.Scened; +using FishNet.Object; +using System.Collections.Generic; +using UnityEngine; + +namespace FishNet.Example.Scened +{ + + /// + /// Loads a single scene, additive scenes, or both when a client + /// enters or exits this trigger. + /// + public class SceneLoaderExample : MonoBehaviour + { + /// + /// True to move the triggering object. + /// + [Tooltip("True to move the triggering object.")] + [SerializeField] + private bool _moveObject = true; + /// + /// True to move all connection objects (clients). + /// + [Tooltip("True to move all connection objects (clients).")] + [SerializeField] + private bool _moveAllObjects; + /// + /// True to replace current scenes with new scenes. First scene loaded will become active scene. + /// + [Tooltip("True to replace current scenes with new scenes. First scene loaded will become active scene.")] + [SerializeField] + private ReplaceOption _replaceOption = ReplaceOption.None; + /// + /// Scenes to load. + /// + [Tooltip("Scenes to load.")] + [SerializeField] + private string[] _scenes = new string[0]; + /// + /// True to only unload for the connectioning causing the trigger. + /// + [Tooltip("True to only unload for the connectioning causing the trigger.")] + [SerializeField] + private bool _connectionOnly; + /// + /// True to automatically unload the loaded scenes when no more connections are using them. + /// + [Tooltip("True to automatically unload the loaded scenes when no more connections are using them.")] + [SerializeField] + private bool _automaticallyUnload = true; + /// + /// True to fire when entering the trigger. False to fire when exiting the trigger. + /// + [Tooltip("True to fire when entering the trigger. False to fire when exiting the trigger.")] + [SerializeField] + private bool _onTriggerEnter = true; + + /// + /// Used to prevent excessive triggering when two clients are loaded and server is separate. + /// Client may enter trigger intentionally then when moved to a new scene will re-enter trigger + /// since original scene will still be loaded on server due to another client being in it. + /// This scenario is extremely unlikely in production but keep it in mind. + /// + private Dictionary _triggeredTimes = new Dictionary(); + + + [Server(Logging = LoggingType.Off)] + private void OnTriggerEnter(Collider other) + { + if (!_onTriggerEnter) + return; + + LoadScene(other.GetComponent()); + } + + [Server(Logging = LoggingType.Off)] + private void OnTriggerExit(Collider other) + { + if (_onTriggerEnter) + return; + + LoadScene(other.GetComponent()); + } + + private void LoadScene(NetworkObject triggeringIdentity) + { + if (!InstanceFinder.NetworkManager.IsServer) + return; + + //NetworkObject isn't necessarily needed but to ensure its the player only run if found. + if (triggeringIdentity == null) + return; + + /* Dont let trigger hit twice by same connection too frequently + * See _triggeredTimes field for more info. */ + if (_triggeredTimes.TryGetValue(triggeringIdentity.Owner, out float time)) + { + if (Time.time - time < 0.5f) + return; + } + _triggeredTimes[triggeringIdentity.Owner] = Time.time; + + //Which objects to move. + List movedObjects = new List(); + if (_moveAllObjects) + { + foreach (NetworkConnection item in InstanceFinder.ServerManager.Clients.Values) + { + foreach (NetworkObject nob in item.Objects) + movedObjects.Add(nob); + } + } + else if (_moveObject) + { + movedObjects.Add(triggeringIdentity); + } + //Load options. + LoadOptions loadOptions = new LoadOptions + { + AutomaticallyUnload = _automaticallyUnload, + }; + + //Make scene data. + SceneLoadData sld = new SceneLoadData(_scenes); + sld.PreferredActiveScene = sld.SceneLookupDatas[0]; + sld.ReplaceScenes = _replaceOption; + sld.Options = loadOptions; + sld.MovedNetworkObjects = movedObjects.ToArray(); + + //Load for connection only. + if (_connectionOnly) + InstanceFinder.SceneManager.LoadConnectionScenes(triggeringIdentity.Owner, sld); + //Load for all clients. + else + InstanceFinder.SceneManager.LoadGlobalScenes(sld); + + + } + + + } + + + + +} \ No newline at end of file diff --git a/Assets/FishNet/Example/All/SceneManager/Scripts/SceneLoaderExample.cs.meta b/Assets/FishNet/Example/All/SceneManager/Scripts/SceneLoaderExample.cs.meta new file mode 100644 index 0000000..ee3fa3f --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Scripts/SceneLoaderExample.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fa23b6e6f9b08d74885e3707aa0d9bc7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/All/SceneManager/Scripts/SceneUnloaderExample.cs b/Assets/FishNet/Example/All/SceneManager/Scripts/SceneUnloaderExample.cs new file mode 100644 index 0000000..67c5af8 --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Scripts/SceneUnloaderExample.cs @@ -0,0 +1,91 @@ +using FishNet.Managing.Logging; +using FishNet.Managing.Scened; +using FishNet.Object; +using UnityEngine; + +namespace FishNet.Example.Scened +{ + + /// + /// Unloads specified scenes when entering or exiting this trigger. + /// + public class SceneUnloaderExample : MonoBehaviour + { + /// + /// Scenes to unload. + /// + [Tooltip("Scenes to unload.")] + [SerializeField] + private string[] _scenes = new string[0]; + /// + /// True to only unload for the connectioning causing the trigger. + /// + [Tooltip("True to only unload for the connectioning causing the trigger.")] + [SerializeField] + private bool _connectionOnly; + /// + /// True to unload unused scenes. + /// + [Tooltip("True to unload unused scenes.")] + [SerializeField] + private bool _unloadUnused = true; + /// + /// True to fire when entering the trigger. False to fire when exiting the trigger. + /// + [Tooltip("True to fire when entering the trigger. False to fire when exiting the trigger.")] + [SerializeField] + private bool _onTriggerEnter = true; + + + [Server(Logging = LoggingType.Off)] + private void OnTriggerEnter(Collider other) + { + if (!_onTriggerEnter) + return; + + UnloadScenes(other.gameObject.GetComponent()); + } + + [Server(Logging = LoggingType.Off)] + private void OnTriggerExit(Collider other) + { + if (_onTriggerEnter) + return; + + UnloadScenes(other.gameObject.GetComponent()); + } + + /// + /// Unload scenes. + /// + /// + private void UnloadScenes(NetworkObject triggeringIdentity) + { + if (!InstanceFinder.NetworkManager.IsServer) + return; + + //NetworkObject isn't necessarily needed but to ensure its the player only run if nob is found. + if (triggeringIdentity == null) + return; + + UnloadOptions unloadOptions = new UnloadOptions() + { + Mode = (_unloadUnused) ? UnloadOptions.ServerUnloadMode.UnloadUnused : UnloadOptions.ServerUnloadMode.KeepUnused + }; + + SceneUnloadData sud = new SceneUnloadData(_scenes); + sud.Options = unloadOptions; + + //Unload only for the triggering connection. + if (_connectionOnly) + InstanceFinder.SceneManager.UnloadConnectionScenes(triggeringIdentity.Owner, sud); + //Unload for all players. + else + InstanceFinder.SceneManager.UnloadGlobalScenes(sud); + } + + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Example/All/SceneManager/Scripts/SceneUnloaderExample.cs.meta b/Assets/FishNet/Example/All/SceneManager/Scripts/SceneUnloaderExample.cs.meta new file mode 100644 index 0000000..ba09def --- /dev/null +++ b/Assets/FishNet/Example/All/SceneManager/Scripts/SceneUnloaderExample.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 84ae572fef1171b41ab287d1c9b5da63 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/FishNet.Example.asmdef b/Assets/FishNet/Example/FishNet.Example.asmdef new file mode 100644 index 0000000..bc50a03 --- /dev/null +++ b/Assets/FishNet/Example/FishNet.Example.asmdef @@ -0,0 +1,15 @@ +{ + "name": "FishNet.Example", + "references": [ + "GUID:7c88a4a7926ee5145ad2dfa06f454c67" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/FishNet/Example/FishNet.Example.asmdef.meta b/Assets/FishNet/Example/FishNet.Example.asmdef.meta new file mode 100644 index 0000000..8b6f01d --- /dev/null +++ b/Assets/FishNet/Example/FishNet.Example.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a5e44165775d9294ba486f89d4c07300 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/Prefabs.meta b/Assets/FishNet/Example/Prefabs.meta new file mode 100644 index 0000000..b4d5992 --- /dev/null +++ b/Assets/FishNet/Example/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9d9b597402966d8498f81aa39427f47a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/Prefabs/NetworkHudCanvas.prefab b/Assets/FishNet/Example/Prefabs/NetworkHudCanvas.prefab new file mode 100644 index 0000000..a0288e4 --- /dev/null +++ b/Assets/FishNet/Example/Prefabs/NetworkHudCanvas.prefab @@ -0,0 +1,529 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &2480283714602906875 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 9139860296052841449} + - component: {fileID: 6745855428185604321} + - component: {fileID: 1424052073409502226} + m_Layer: 5 + m_Name: Indicator + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &9139860296052841449 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2480283714602906875} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -1} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4393252311501663115} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &6745855428185604321 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2480283714602906875} + m_CullTransparentMesh: 0 +--- !u!114 &1424052073409502226 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2480283714602906875} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.25490198, g: 0.25490198, b: 0.25490198, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 2b3dca501a9d8c8479dc71dd068aa8b8, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!1 &4393252310969058995 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4393252310969058990} + - component: {fileID: 4393252310969058994} + - component: {fileID: 4393252310969058991} + - component: {fileID: 4393252310969058988} + - component: {fileID: 4393252310969058989} + m_Layer: 5 + m_Name: NetworkHudCanvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4393252310969058990 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252310969058995} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_Children: + - {fileID: 4393252311652982283} + - {fileID: 4393252311501663115} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!114 &4393252310969058994 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252310969058995} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6d3606bfdac5a4743890fc1a5ecd8f24, type: 3} + m_Name: + m_EditorClassIdentifier: + AutoStart: 0 + _stoppedColor: {r: 0.25490198, g: 0.25490198, b: 0.25490198, a: 1} + _changingColor: {r: 0.78431374, g: 0.6862745, b: 0, a: 1} + _startedColor: {r: 0, g: 0.5882353, b: 0.64705884, a: 1} + _serverIndicator: {fileID: 3965864433427628056} + _clientIndicator: {fileID: 1424052073409502226} +--- !u!223 &4393252310969058991 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252310969058995} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!114 &4393252310969058988 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252310969058995} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 1 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 1920, y: 1080} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0.5 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 +--- !u!114 &4393252310969058989 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252310969058995} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!1 &4393252311501663112 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4393252311501663115} + - component: {fileID: 4393252311501663108} + - component: {fileID: 4393252311501663109} + - component: {fileID: 4393252311501663114} + m_Layer: 5 + m_Name: Client + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4393252311501663115 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311501663112} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 9139860296052841449} + m_Father: {fileID: 4393252310969058990} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 16, y: -96} + m_SizeDelta: {x: 256, y: 64} + m_Pivot: {x: 0, y: 1} +--- !u!222 &4393252311501663108 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311501663112} + m_CullTransparentMesh: 0 +--- !u!114 &4393252311501663109 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311501663112} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 2d50394614f8feb4eb0567fb7618d84d, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &4393252311501663114 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311501663112} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 4393252311501663109} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 4393252310969058994} + m_MethodName: OnClick_Client + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!1 &4393252311652982280 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4393252311652982283} + - component: {fileID: 4393252311652982276} + - component: {fileID: 4393252311652982277} + - component: {fileID: 4393252311652982282} + m_Layer: 5 + m_Name: Server + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4393252311652982283 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311652982280} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 7233259200663826443} + m_Father: {fileID: 4393252310969058990} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 16, y: -16} + m_SizeDelta: {x: 256, y: 64} + m_Pivot: {x: 0, y: 1} +--- !u!222 &4393252311652982276 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311652982280} + m_CullTransparentMesh: 0 +--- !u!114 &4393252311652982277 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311652982280} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 1b187e63031bf7849b249c8212440c3b, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &4393252311652982282 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4393252311652982280} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 4393252311652982277} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 4393252310969058994} + m_MethodName: OnClick_Server + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!1 &4808982256197118437 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7233259200663826443} + - component: {fileID: 5104387649082666082} + - component: {fileID: 3965864433427628056} + m_Layer: 5 + m_Name: Indicator + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &7233259200663826443 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4808982256197118437} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -1} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4393252311652982283} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &5104387649082666082 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4808982256197118437} + m_CullTransparentMesh: 0 +--- !u!114 &3965864433427628056 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4808982256197118437} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.25490198, g: 0.25490198, b: 0.25490198, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 2b3dca501a9d8c8479dc71dd068aa8b8, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 diff --git a/Assets/FishNet/Example/Prefabs/NetworkHudCanvas.prefab.meta b/Assets/FishNet/Example/Prefabs/NetworkHudCanvas.prefab.meta new file mode 100644 index 0000000..c4281bb --- /dev/null +++ b/Assets/FishNet/Example/Prefabs/NetworkHudCanvas.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0570b6f7f713dc44a90463654bbcd8d0 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/Prefabs/NetworkManager.prefab b/Assets/FishNet/Example/Prefabs/NetworkManager.prefab new file mode 100644 index 0000000..fce3e55 --- /dev/null +++ b/Assets/FishNet/Example/Prefabs/NetworkManager.prefab @@ -0,0 +1,208 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &7443408887813606051 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7443408887813606049} + - component: {fileID: 7443408887813606050} + - component: {fileID: 934570884} + - component: {fileID: 7443408887813606060} + m_Layer: 0 + m_Name: NetworkManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7443408887813606049 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408887813606051} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4393252310584637084} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &7443408887813606050 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408887813606051} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d2c95dfde7d73b54dbbdc23155d35d36, type: 3} + m_Name: + m_EditorClassIdentifier: + _logging: {fileID: 0} + _spawnablePrefabs: {fileID: 11400000, guid: ec64eb18c93ab344892891f33edbf82a, type: 2} + _refreshDefaultPrefabs: 0 + _runInBackground: 1 + _dontDestroyOnLoad: 1 + _persistence: 0 +--- !u!114 &934570884 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408887813606051} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7d331f979d46e8e4a9fc90070c596d44, type: 3} + m_Name: + m_EditorClassIdentifier: + _defaultConditions: + - {fileID: 11400000, guid: 2033f54fd2794464bae08fa5a55c8996, type: 2} +--- !u!114 &7443408887813606060 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408887813606051} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 211a9f6ec51ddc14f908f5acc0cd0423, type: 3} + m_Name: + m_EditorClassIdentifier: + _playerPrefab: {fileID: 0} + _addToDefaultScene: 1 + Spawns: [] +--- !u!1001 &2130063410 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 7443408887813606049} + m_Modifications: + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_Pivot.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_Pivot.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_RootOrder + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_AnchorMax.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_AnchorMin.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_SizeDelta.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_AnchoredPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4393252310969058995, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + propertyPath: m_Name + value: NetworkHudCanvas + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3} +--- !u!224 &4393252310584637084 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, + type: 3} + m_PrefabInstance: {fileID: 2130063410} + m_PrefabAsset: {fileID: 0} diff --git a/Assets/FishNet/Example/Prefabs/NetworkManager.prefab.meta b/Assets/FishNet/Example/Prefabs/NetworkManager.prefab.meta new file mode 100644 index 0000000..ae648e6 --- /dev/null +++ b/Assets/FishNet/Example/Prefabs/NetworkManager.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0b650fca685f2eb41a86538aa883e4c1 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/Scripts.meta b/Assets/FishNet/Example/Scripts.meta new file mode 100644 index 0000000..4feee45 --- /dev/null +++ b/Assets/FishNet/Example/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7a4f0be731a103e4888691005f67a0e1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Example/Scripts/NetworkHudCanvases.cs b/Assets/FishNet/Example/Scripts/NetworkHudCanvases.cs new file mode 100644 index 0000000..bd11edf --- /dev/null +++ b/Assets/FishNet/Example/Scripts/NetworkHudCanvases.cs @@ -0,0 +1,248 @@ +using FishNet.Managing; +using FishNet.Transporting; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; + +public class NetworkHudCanvases : MonoBehaviour +{ + #region Types. + /// + /// Ways the HUD will automatically start a connection. + /// + private enum AutoStartType + { + Disabled, + Host, + Server, + Client + } + #endregion + + #region Serialized. + /// + /// What connections to automatically start on play. + /// + [Tooltip("What connections to automatically start on play.")] + [SerializeField] + private AutoStartType _autoStartType = AutoStartType.Disabled; + /// + /// Color when socket is stopped. + /// + [Tooltip("Color when socket is stopped.")] + [SerializeField] + private Color _stoppedColor; + /// + /// Color when socket is changing. + /// + [Tooltip("Color when socket is changing.")] + [SerializeField] + private Color _changingColor; + /// + /// Color when socket is started. + /// + [Tooltip("Color when socket is started.")] + [SerializeField] + private Color _startedColor; + [Header("Indicators")] + /// + /// Indicator for server state. + /// + [Tooltip("Indicator for server state.")] + [SerializeField] + private Image _serverIndicator; + /// + /// Indicator for client state. + /// + [Tooltip("Indicator for client state.")] + [SerializeField] + private Image _clientIndicator; + #endregion + + #region Private. + /// + /// Found NetworkManager. + /// + private NetworkManager _networkManager; + /// + /// Current state of client socket. + /// + private LocalConnectionState _clientState = LocalConnectionState.Stopped; + /// + /// Current state of server socket. + /// + private LocalConnectionState _serverState = LocalConnectionState.Stopped; +#if !ENABLE_INPUT_SYSTEM + /// + /// EventSystem for the project. + /// + private EventSystem _eventSystem; +#endif + #endregion + + void OnGUI() + { +#if ENABLE_INPUT_SYSTEM + string GetNextStateText(LocalConnectionState state) + { + if (state == LocalConnectionState.Stopped) + return "Start"; + else if (state == LocalConnectionState.Starting) + return "Starting"; + else if (state == LocalConnectionState.Stopping) + return "Stopping"; + else if (state == LocalConnectionState.Started) + return "Stop"; + else + return "Invalid"; + } + + GUILayout.BeginArea(new Rect(16, 16, 256, 9000)); + Vector2 defaultResolution = new Vector2(1920f, 1080f); + GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(Screen.width / defaultResolution.x, Screen.height / defaultResolution.y, 1)); + + GUIStyle style = GUI.skin.GetStyle("button"); + int originalFontSize = style.fontSize; + + Vector2 buttonSize = new Vector2(256f, 64f); + style.fontSize = 28; + //Server button. + if (Application.platform != RuntimePlatform.WebGLPlayer) + { + if (GUILayout.Button($"{GetNextStateText(_serverState)} Server", GUILayout.Width(buttonSize.x), GUILayout.Height(buttonSize.y))) + OnClick_Server(); + GUILayout.Space(10f); + } + + //Client button. + if (GUILayout.Button($"{GetNextStateText(_clientState)} Client", GUILayout.Width(buttonSize.x), GUILayout.Height(buttonSize.y))) + OnClick_Client(); + + style.fontSize = originalFontSize; + + GUILayout.EndArea(); +#endif + } + + private void Start() + { +#if !ENABLE_INPUT_SYSTEM + SetEventSystem(); + BaseInputModule inputModule = FindObjectOfType(); + if (inputModule == null) + gameObject.AddComponent(); +#else + _serverIndicator.transform.parent.gameObject.SetActive(false); + _clientIndicator.transform.parent.gameObject.SetActive(false); +#endif + + _networkManager = FindObjectOfType(); + if (_networkManager == null) + { + Debug.LogError("NetworkManager not found, HUD will not function."); + return; + } + else + { + UpdateColor(LocalConnectionState.Stopped, ref _serverIndicator); + UpdateColor(LocalConnectionState.Stopped, ref _clientIndicator); + _networkManager.ServerManager.OnServerConnectionState += ServerManager_OnServerConnectionState; + _networkManager.ClientManager.OnClientConnectionState += ClientManager_OnClientConnectionState; + } + + if (_autoStartType == AutoStartType.Host || _autoStartType == AutoStartType.Server) + OnClick_Server(); + if (!Application.isBatchMode && (_autoStartType == AutoStartType.Host || _autoStartType == AutoStartType.Client)) + OnClick_Client(); + } + + + private void OnDestroy() + { + if (_networkManager == null) + return; + + _networkManager.ServerManager.OnServerConnectionState -= ServerManager_OnServerConnectionState; + _networkManager.ClientManager.OnClientConnectionState -= ClientManager_OnClientConnectionState; + } + + /// + /// Updates img color baased on state. + /// + /// + /// + private void UpdateColor(LocalConnectionState state, ref Image img) + { + Color c; + if (state == LocalConnectionState.Started) + c = _startedColor; + else if (state == LocalConnectionState.Stopped) + c = _stoppedColor; + else + c = _changingColor; + + img.color = c; + } + + + private void ClientManager_OnClientConnectionState(ClientConnectionStateArgs obj) + { + _clientState = obj.ConnectionState; + UpdateColor(obj.ConnectionState, ref _clientIndicator); + } + + + private void ServerManager_OnServerConnectionState(ServerConnectionStateArgs obj) + { + _serverState = obj.ConnectionState; + UpdateColor(obj.ConnectionState, ref _serverIndicator); + } + + + public void OnClick_Server() + { + if (_networkManager == null) + return; + + if (_serverState != LocalConnectionState.Stopped) + _networkManager.ServerManager.StopConnection(true); + else + _networkManager.ServerManager.StartConnection(); + + DeselectButtons(); + } + + + public void OnClick_Client() + { + if (_networkManager == null) + return; + + if (_clientState != LocalConnectionState.Stopped) + _networkManager.ClientManager.StopConnection(); + else + _networkManager.ClientManager.StartConnection(); + + DeselectButtons(); + } + + + private void SetEventSystem() + { +#if !ENABLE_INPUT_SYSTEM + if (_eventSystem != null) + return; + _eventSystem = FindObjectOfType(); + if (_eventSystem == null) + _eventSystem = gameObject.AddComponent(); +#endif + } + + private void DeselectButtons() + { +#if !ENABLE_INPUT_SYSTEM + SetEventSystem(); + _eventSystem?.SetSelectedGameObject(null); +#endif + } +} \ No newline at end of file diff --git a/Assets/FishNet/Example/Scripts/NetworkHudCanvases.cs.meta b/Assets/FishNet/Example/Scripts/NetworkHudCanvases.cs.meta new file mode 100644 index 0000000..e2e25fb --- /dev/null +++ b/Assets/FishNet/Example/Scripts/NetworkHudCanvases.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6d3606bfdac5a4743890fc1a5ecd8f24 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/LICENSE.txt b/Assets/FishNet/LICENSE.txt new file mode 100644 index 0000000..a76aaf2 --- /dev/null +++ b/Assets/FishNet/LICENSE.txt @@ -0,0 +1,35 @@ +0. Definitions. + +"Fish-Net" means FishNet, FishNetworking, Fish-Networking, networking for Unity Engine. + +"Repository Service" means the respository service through which Fish-Net is made available. + +“Software” means the software (including code in source or object format as applicable) of Fish-Net that accompanies this License. + +"FirstGearGames" means Benjamin Berwick of FirstGearGames LLC, registered 2018, North Carolina. + +1. License Grant to the Software. FirstGearGames grants to you a worldwide, non-exclusive, no-charge, and royalty-free license to reproduce, modify, and use the Software for developed game, or other content with Software. + +1.1 Exclusions. Other products of like Software (eg: networking solutions) may not use, reverse engineer, or implement Software in part or full. Exclusions do not apply to parts of Software which are governed by a third-party license, nor to content or works created specifically to be used with Software, such as tools, add-ons, games, or improvements for Software. + +2. Trademarks. You are not granted any right or license under this License to use any trademarks, service marks, trade names, products names, or branding of FirstGearGames or its affiliates (“Trademarks”). + +3. Notice & Third-Party Terms. This License, including notices of copyright associated with the Software, must be provided in all substantial portions of the Software (or, if that is impracticable, in any other location where such notices are customarily placed). If the Software is accompanied by a “third-party notices” or similar file, you acknowledge and agree that software identified in that file is governed exclusively by those separate license terms. + +4. DISCLAIMER, LIMITATION OF LIABILITY. THE SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND IS PROVIDED WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND/OR NONINFRINGEMENT. IN NO EVENT SHALL ANY COPYRIGHT HOLDER OR AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES (WHETHER DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL, INCLUDING PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF USE, DATA, OR PROFITS, AND BUSINESS INTERRUPTION), OR OTHER LIABILITY WHATSOEVER, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM OR OUT OF, OR IN CONNECTION WITH, THE SOFTWARE OR THE USE OF OR OTHER DEALINGS IN IT, EVEN WHERE ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +5. USE IS ACCEPTANCE and License Versions. Your access to and use of the Software, and/or any other indications of acceptance where required, constitutes your acceptance of this License and its terms and conditions. This License may be modified or updated; upon any such modification or update, you will comply with the terms of the updated License for any use of any of the Software under the updated License. + +6. Use in Compliance with Law and Termination. + +6.1 Compliance. Your exercise of the license granted herein will at all times be in compliance with applicable law and will not infringe any proprietary rights (including intellectual property rights). + +6.2 Termination. This License will terminate immediately (i) on any breach by you of this License; and (ii) if you commence any form of patent litigation, including a cross-claim or counterclaim, against anyone wherein you allege that the Software constitutes direct or secondary/indirect patent infringement. + +7. Severability. If any provision of this License is held to be unenforceable or invalid, that provision will be enforced to the maximum extent possible and the other provisions will remain in full force and effect. + +8. Governing Law and Venue. This License is governed by and construed in accordance with the laws of North Carolina, United States. You and FirstGearGames agree to submit to the personal and exclusive jurisdiction of and venue in the state and federal courts located in Onslow County, North Carolina concerning any dispute arising out of this License (“Dispute”). + +9. You agree by submitting ideas, modifications, or changes (content) of any kind to Software within Repository Service does not grant you ownership to Software nor additional rights to Software. + +9.1 By submitting to Repository Service you are granting FirstGearGames with a no-charge, and royalty-free license to reproduce, modify, and use submitted content. \ No newline at end of file diff --git a/Assets/FishNet/LICENSE.txt.meta b/Assets/FishNet/LICENSE.txt.meta new file mode 100644 index 0000000..5ba5b4f --- /dev/null +++ b/Assets/FishNet/LICENSE.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 83c5a6d0014103d48a0028f0ab7fec8d +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Plugins.meta b/Assets/FishNet/Plugins.meta new file mode 100644 index 0000000..efa9301 --- /dev/null +++ b/Assets/FishNet/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 42cadfdebb9b32c42941efb731593966 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Plugins/CodeAnalysis.meta b/Assets/FishNet/Plugins/CodeAnalysis.meta new file mode 100644 index 0000000..a7fb576 --- /dev/null +++ b/Assets/FishNet/Plugins/CodeAnalysis.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 90f5de37ae0e3184fb0d662879ba060b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Plugins/CodeAnalysis/FishNet.CodeAnalysis.Analyzers.dll b/Assets/FishNet/Plugins/CodeAnalysis/FishNet.CodeAnalysis.Analyzers.dll new file mode 100644 index 0000000..c01e330 Binary files /dev/null and b/Assets/FishNet/Plugins/CodeAnalysis/FishNet.CodeAnalysis.Analyzers.dll differ diff --git a/Assets/FishNet/Plugins/CodeAnalysis/FishNet.CodeAnalysis.Analyzers.dll.meta b/Assets/FishNet/Plugins/CodeAnalysis/FishNet.CodeAnalysis.Analyzers.dll.meta new file mode 100644 index 0000000..73913eb --- /dev/null +++ b/Assets/FishNet/Plugins/CodeAnalysis/FishNet.CodeAnalysis.Analyzers.dll.meta @@ -0,0 +1,72 @@ +fileFormatVersion: 2 +guid: 1907658b89c1bbe42a0063df40b7ca24 +labels: +- RoslynAnalyzer +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: + - UNITY_2020_3_OR_NEWER + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Plugins/CodeAnalysis/FishNet.CodeAnalysis.dll b/Assets/FishNet/Plugins/CodeAnalysis/FishNet.CodeAnalysis.dll new file mode 100644 index 0000000..cdec54e Binary files /dev/null and b/Assets/FishNet/Plugins/CodeAnalysis/FishNet.CodeAnalysis.dll differ diff --git a/Assets/FishNet/Plugins/CodeAnalysis/FishNet.CodeAnalysis.dll.meta b/Assets/FishNet/Plugins/CodeAnalysis/FishNet.CodeAnalysis.dll.meta new file mode 100644 index 0000000..9d99248 --- /dev/null +++ b/Assets/FishNet/Plugins/CodeAnalysis/FishNet.CodeAnalysis.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 620557a5e202e644cb322b8fcc9422ea +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Plugins/CodeAnalysis/LICENSE.txt b/Assets/FishNet/Plugins/CodeAnalysis/LICENSE.txt new file mode 100644 index 0000000..8223fdb --- /dev/null +++ b/Assets/FishNet/Plugins/CodeAnalysis/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Abdelfattah-Radwan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Assets/FishNet/Plugins/CodeAnalysis/LICENSE.txt.meta b/Assets/FishNet/Plugins/CodeAnalysis/LICENSE.txt.meta new file mode 100644 index 0000000..3682f59 --- /dev/null +++ b/Assets/FishNet/Plugins/CodeAnalysis/LICENSE.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 93ef12b9e040fa8429d9ef686212ed4e +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Plugins/CodeAnalysis/README.txt b/Assets/FishNet/Plugins/CodeAnalysis/README.txt new file mode 100644 index 0000000..a372cd4 --- /dev/null +++ b/Assets/FishNet/Plugins/CodeAnalysis/README.txt @@ -0,0 +1,2 @@ +Git URL: +https://github.com/Abdelfattah-Radwan/FishNet.CodeAnalysis \ No newline at end of file diff --git a/Assets/FishNet/Plugins/CodeAnalysis/README.txt.meta b/Assets/FishNet/Plugins/CodeAnalysis/README.txt.meta new file mode 100644 index 0000000..190da78 --- /dev/null +++ b/Assets/FishNet/Plugins/CodeAnalysis/README.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 835336ee0aec7ef41a1cfda40886f443 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime.meta b/Assets/FishNet/Runtime.meta new file mode 100644 index 0000000..b353727 --- /dev/null +++ b/Assets/FishNet/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bf3413dc76bc5ad45904d2f8166257ef +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Authenticating.meta b/Assets/FishNet/Runtime/Authenticating.meta new file mode 100644 index 0000000..6915aae --- /dev/null +++ b/Assets/FishNet/Runtime/Authenticating.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0fcbb1078d7aa6d4c808c7913ed26092 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Authenticating/Authenticator.cs b/Assets/FishNet/Runtime/Authenticating/Authenticator.cs new file mode 100644 index 0000000..9272131 --- /dev/null +++ b/Assets/FishNet/Runtime/Authenticating/Authenticator.cs @@ -0,0 +1,51 @@ +using FishNet.Connection; +using FishNet.Managing; +using System; +using UnityEngine; + +namespace FishNet.Authenticating +{ + /// + /// When inherited from this can be used to create a custom authentication process before clients may communicate with the server. + /// + public abstract class Authenticator : MonoBehaviour + { + #region Public. + /// + /// True if this authenticator has been intiialzied. + /// + public bool Initialized { get; private set; } + #endregion + + #region Protected. + /// + /// NetworkManager for this Authenticator. + /// + protected NetworkManager NetworkManager { get; private set; } + #endregion + + /// + /// Called when authenticator has concluded a result for a connection. Boolean is true if authentication passed, false if failed. + /// Server listens for this event automatically. + /// + public abstract event Action OnAuthenticationResult; + + /// + /// Initializes this script for use. + /// + /// + public virtual void InitializeOnce(NetworkManager networkManager) + { + NetworkManager = networkManager; + Initialized = true; + } + + /// + /// Called on the server immediately after a client connects. Can be used to send data to the client for authentication. + /// + /// Connection which is not yet authenticated. + public virtual void OnRemoteConnection(NetworkConnection connection) { } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Authenticating/Authenticator.cs.meta b/Assets/FishNet/Runtime/Authenticating/Authenticator.cs.meta new file mode 100644 index 0000000..fe94f3f --- /dev/null +++ b/Assets/FishNet/Runtime/Authenticating/Authenticator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a9adfb82407774645a1f455ceb9298f9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Broadcast.meta b/Assets/FishNet/Runtime/Broadcast.meta new file mode 100644 index 0000000..2268277 --- /dev/null +++ b/Assets/FishNet/Runtime/Broadcast.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 25caa1205d1af6742a85ede29e1a672e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Broadcast/Helping.meta b/Assets/FishNet/Runtime/Broadcast/Helping.meta new file mode 100644 index 0000000..06cb025 --- /dev/null +++ b/Assets/FishNet/Runtime/Broadcast/Helping.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8d0473552b6e6834abb8a336d060797a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Broadcast/Helping/BroadcastHelper.cs b/Assets/FishNet/Runtime/Broadcast/Helping/BroadcastHelper.cs new file mode 100644 index 0000000..c1452da --- /dev/null +++ b/Assets/FishNet/Runtime/Broadcast/Helping/BroadcastHelper.cs @@ -0,0 +1,19 @@ +using FishNet.Object.Helping; + +namespace FishNet.Broadcast.Helping +{ + internal static class BroadcastHelper + { + /// + /// Gets the key for a broadcast type. + /// + /// + /// + /// + public static ushort GetKey() + { + return typeof(T).FullName.GetStableHash16(); + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Broadcast/Helping/BroadcastHelper.cs.meta b/Assets/FishNet/Runtime/Broadcast/Helping/BroadcastHelper.cs.meta new file mode 100644 index 0000000..c8e4514 --- /dev/null +++ b/Assets/FishNet/Runtime/Broadcast/Helping/BroadcastHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c314af08d630630449b7b7af740b9c7d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Broadcast/IBroadcast.cs b/Assets/FishNet/Runtime/Broadcast/IBroadcast.cs new file mode 100644 index 0000000..5fe07f2 --- /dev/null +++ b/Assets/FishNet/Runtime/Broadcast/IBroadcast.cs @@ -0,0 +1,8 @@ + +namespace FishNet.Broadcast +{ + /// + /// Include this interface on types intended to be used with Broadcast. + /// + public interface IBroadcast { } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Broadcast/IBroadcast.cs.meta b/Assets/FishNet/Runtime/Broadcast/IBroadcast.cs.meta new file mode 100644 index 0000000..c63e51a --- /dev/null +++ b/Assets/FishNet/Runtime/Broadcast/IBroadcast.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 88ec864df25feed49bdcdab7f880531d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Config.json b/Assets/FishNet/Runtime/Config.json new file mode 100644 index 0000000..a24f387 --- /dev/null +++ b/Assets/FishNet/Runtime/Config.json @@ -0,0 +1 @@ +{"StripReleaseBuilds":true} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Config.json.meta b/Assets/FishNet/Runtime/Config.json.meta new file mode 100644 index 0000000..baa766c --- /dev/null +++ b/Assets/FishNet/Runtime/Config.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9f1ece47c2d48194ea4827bf592a2279 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Connection.meta b/Assets/FishNet/Runtime/Connection.meta new file mode 100644 index 0000000..4658695 --- /dev/null +++ b/Assets/FishNet/Runtime/Connection.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bc8933915f599d44d98ae151e3fd0da7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Connection/Buffer.cs b/Assets/FishNet/Runtime/Connection/Buffer.cs new file mode 100644 index 0000000..404d31e --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/Buffer.cs @@ -0,0 +1,259 @@ +using FishNet.Managing; +using FishNet.Managing.Logging; +using FishNet.Managing.Transporting; +using FishNet.Serializing; +using FishNet.Utility.Performance; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace FishNet.Connection +{ + /// + /// A byte buffer that automatically resizes. + /// + internal class ByteBuffer + { + /// + /// How many more bytes may fit into the buffer. + /// + internal int Remaining => (Size - Length); + /// + /// Buffer data. + /// + internal byte[] Data { get; private set; } + /// + /// How many bytes currently into Data. This will include the reserve. + /// + internal int Length { get; private set; } + /// + /// Size of the buffer. Data.Length may exceed this value as it uses a pooled array. + /// + internal int Size { get; private set; } + /// + /// True if data has been written. + /// + internal bool HasData { get; private set; } + /// + /// Bytes to reserve when resetting. + /// + private int _reserve; + + internal ByteBuffer(int size, int reserve = 0) + { + Data = ByteArrayPool.Retrieve(size); + Size = size; + _reserve = reserve; + Reset(); + } + + public void Dispose() + { + if (Data != null) + ByteArrayPool.Store(Data); + Data = null; + } + + /// + /// Resets instance without clearing Data. + /// + internal void Reset() + { + Length = _reserve; + HasData = false; + } + + /// + /// Copies segments without error checking, including tick for the first time data is added. + /// + /// + internal void CopySegment(uint tick, ArraySegment segment) + { + /* If data has not been written to buffer yet + * then write tick to the start. */ + if (!HasData) + { + int pos = 0; + WriterExtensions.WriteUInt32(Data, tick, ref pos); + } + + Buffer.BlockCopy(segment.Array, segment.Offset, Data, Length, segment.Count); + Length += segment.Count; + HasData = true; + } + /// + /// Copies segments without error checking. + /// + /// + internal void CopySegment(ArraySegment segment) + { + Buffer.BlockCopy(segment.Array, segment.Offset, Data, Length, segment.Count); + Length += segment.Count; + HasData = true; + } + + } + + internal class PacketBundle + { + /// + /// True if data has been written. + /// + internal bool HasData => _buffers[0].HasData; + /// + /// All buffers written. Collection is not cleared when reset but rather the index in which to write is. + /// + private List _buffers = new List(); + /// + /// Buffer which is being written to. + /// + private int _bufferIndex; + /// + /// Maximum size packet the transport can handle. + /// + private int _maximumTransportUnit; + /// + /// Number of buffers written to. Will return 0 if nothing has been written. + /// + public int WrittenBuffers => (!HasData) ? 0 : (_bufferIndex + 1); + /// + /// Number of bytes to reserve at the beginning of each buffer. + /// + private int _reserve; + /// + /// NetworkManager this is for. + /// + private NetworkManager _networkManager; + + internal PacketBundle(NetworkManager manager, int mtu, int reserve = 0) + { + //Allow bytes for the tick. + reserve += TransportManager.TICK_BYTES; + + _networkManager = manager; + _maximumTransportUnit = mtu; + _reserve = reserve; + AddBuffer(); + + Reset(); + } + + public void Dispose() + { + for (int i = 0; i < _buffers.Count; i++) + _buffers[i].Dispose(); + } + + /// + /// Adds a buffer using current settings. + /// + private ByteBuffer AddBuffer() + { + ByteBuffer ba = new ByteBuffer(_maximumTransportUnit, _reserve); + _buffers.Add(ba); + return ba; + } + + /// + /// Resets using current settings. + /// + internal void Reset() + { + _bufferIndex = 0; + + for (int i = 0; i < _buffers.Count; i++) + _buffers[i].Reset(); + } + + /// + /// Writes a segment to this packet bundle using the current WriteIndex. + /// + /// True to force data into a new buffer. + internal void Write(ArraySegment segment, bool forceNewBuffer = false) + { + //Nothing to be written. + if (segment.Count == 0) + return; + + /* If the segment count is larger than the mtu then + * something went wrong. Nothing should call this method + * directly except the TransportManager, which will automatically + * split packets that exceed MTU into reliable ordered. */ + if (segment.Count > _maximumTransportUnit) + { + _networkManager.LogError($"Segment is length of {segment.Count} while MTU is {_maximumTransportUnit}. Packet was not split properly and will not be sent."); + return; + } + + ByteBuffer ba = _buffers[_bufferIndex]; + /* Make a new buffer if... + * forcing a new buffer and data has already been written to the current. + * or--- + * segment.Count is more than what is remaining in the buffer. */ + bool useNewBuffer = (forceNewBuffer && ba.Length > _reserve) || + (segment.Count > ba.Remaining); + if (useNewBuffer) + { + _bufferIndex++; + //If need to make a new buffer then do so. + if (_buffers.Count <= _bufferIndex) + { + ba = AddBuffer(); + } + else + { + ba = _buffers[_bufferIndex]; + ba.Reset(); + } + } + + uint tick = _networkManager.TimeManager.LocalTick; + ba.CopySegment(tick, segment); + } + + /// + /// Gets a buffer for the specified index. Returns true and outputs the buffer if it was successfully found. + /// + /// Index of the buffer to retrieve. + /// Buffer retrieved from the list. Null if the specified buffer was not found. + internal bool GetBuffer(int index, out ByteBuffer bb) + { + bb = null; + + if (index >= _buffers.Count || index < 0) + { + _networkManager.LogError($"Index of {index} is out of bounds. There are {_buffers.Count} available."); + return false; + } + if (index > _bufferIndex) + { + _networkManager.LogError($"Index of {index} exceeds the number of written buffers. There are {WrittenBuffers} written buffers."); + return false; + } + + bb = _buffers[index]; + return bb.HasData; + } + + /// + /// Returns a PacketBundle for a channel. ResetPackets must be called afterwards. + /// + /// + /// True if PacketBundle is valid on the index and contains data. + internal static bool GetPacketBundle(int channel, List bundles, out PacketBundle mtuBuffer) + { + //Out of bounds. + if (channel >= bundles.Count) + { + mtuBuffer = null; + return false; + } + + mtuBuffer = bundles[channel]; + return mtuBuffer.HasData; + } + } + + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Connection/Buffer.cs.meta b/Assets/FishNet/Runtime/Connection/Buffer.cs.meta new file mode 100644 index 0000000..85d9ae8 --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/Buffer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eb2f3ce9b5ac27f40b7daa9364fb4d60 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Connection/NetworkConnection.Buffer.cs b/Assets/FishNet/Runtime/Connection/NetworkConnection.Buffer.cs new file mode 100644 index 0000000..76721e3 --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.Buffer.cs @@ -0,0 +1,110 @@ +using FishNet.Broadcast; +using FishNet.Managing; +using FishNet.Managing.Logging; +using FishNet.Managing.Transporting; +using FishNet.Transporting; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace FishNet.Connection +{ + public partial class NetworkConnection + { + + #region Private. + /// + /// PacketBundles to send to this connection. An entry will be made for each channel. + /// + private List _toClientBundles = new List(); + /// + /// True if this object has been dirtied. + /// + private bool _serverDirtied; + #endregion + + /// + /// Initializes this script. + /// + private void InitializeBuffer() + { + for (byte i = 0; i < TransportManager.CHANNEL_COUNT; i++) + { + int mtu = NetworkManager.TransportManager.GetLowestMTU(i); + _toClientBundles.Add(new PacketBundle(NetworkManager, mtu)); + } + } + + + /// + /// Sends a broadcast to this connection. + /// + /// Type of broadcast to send. + /// Broadcast data being sent; for example: an instance of your broadcast type. + /// True if the client must be authenticated for this broadcast to send. + /// Channel to send on. + public void Broadcast(T message, bool requireAuthenticated = true, Channel channel = Channel.Reliable) where T : struct, IBroadcast + { + if (!IsActive) + NetworkManager.LogError($"Connection is not valid, cannot send broadcast."); + else + NetworkManager.ServerManager.Broadcast(this, message, requireAuthenticated, channel); + } + + /// + /// Sends data from the server to a client. + /// + /// True to force data into a new buffer. + internal void SendToClient(byte channel, ArraySegment segment, bool forceNewBuffer = false) + { + //Cannot send data when disconnecting. + if (Disconnecting) + return; + + if (!IsActive) + { + NetworkManager.LogWarning($"Data cannot be sent to connection {ClientId} because it is not active."); + return; + } + //If channel is out of bounds then default to the first channel. + if (channel >= _toClientBundles.Count) + channel = 0; + + _toClientBundles[channel].Write(segment, forceNewBuffer); + ServerDirty(); + } + + /// + /// Returns a PacketBundle for a channel. ResetPackets must be called afterwards. + /// + /// + /// True if PacketBundle is valid on the index and contains data. + internal bool GetPacketBundle(int channel, out PacketBundle packetBundle) + { + return PacketBundle.GetPacketBundle(channel, _toClientBundles, out packetBundle); + } + + /// + /// Indicates the server has data to send to this connection. + /// + private void ServerDirty() + { + bool wasDirty = _serverDirtied; + _serverDirtied = true; + + //If not yet dirty then tell transport manager this is dirty. + if (!wasDirty) + NetworkManager.TransportManager.ServerDirty(this); + } + + /// + /// Resets that there is data to send. + /// + internal void ResetServerDirty() + { + _serverDirtied = false; + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Connection/NetworkConnection.Buffer.cs.meta b/Assets/FishNet/Runtime/Connection/NetworkConnection.Buffer.cs.meta new file mode 100644 index 0000000..2b3f00e --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.Buffer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5bc17ee5bac499347a6fbad9dd24b7b0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Connection/NetworkConnection.LOD.cs b/Assets/FishNet/Runtime/Connection/NetworkConnection.LOD.cs new file mode 100644 index 0000000..771e2ad --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.LOD.cs @@ -0,0 +1,48 @@ +using FishNet.Object; +using System; +using System.Collections.Generic; + +namespace FishNet.Connection +{ + + /// + /// A container for a connected client used to perform actions on and gather information for the declared client. + /// + public partial class NetworkConnection : IEquatable + { + /// + /// Level of detail for each NetworkObject. + /// Since this is called frequently this field is intentionally not an accessor to increase performance. + /// + public Dictionary LevelOfDetails = new Dictionary(); + /// + /// Number oftimes this connection may send a forced LOD update. + /// + internal int AllowedForcedLodUpdates; + /// + /// Last tick an LOD was sent. + /// On client and clientHost this is LocalTick. + /// On server only this is LastPacketTick for the connection. + /// + internal uint LastLevelOfDetailUpdate; + /// + /// Returns if the client has not sent an LOD update for expectedInterval. + /// + /// + internal bool IsLateForLevelOfDetail(uint expectedInterval) + { + //Local client is immune since server and client share ticks. + if (IsLocalClient) + return false; + + return ((LastPacketTick - LastLevelOfDetailUpdate) > expectedInterval); + } + + /// + /// Number of level of detail update infractions for this connection. + /// + internal int LevelOfDetailInfractions; + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Connection/NetworkConnection.LOD.cs.meta b/Assets/FishNet/Runtime/Connection/NetworkConnection.LOD.cs.meta new file mode 100644 index 0000000..ada1054 --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.LOD.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d9aba23f6fe8fd849a39712926efe055 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Connection/NetworkConnection.PingPong.cs b/Assets/FishNet/Runtime/Connection/NetworkConnection.PingPong.cs new file mode 100644 index 0000000..5e2feb4 --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.PingPong.cs @@ -0,0 +1,103 @@ +using FishNet.Managing; +using FishNet.Managing.Timing; +using System; +using UnityEngine; + +namespace FishNet.Connection +{ + + /// + /// A container for a connected client used to perform actions on and gather information for the declared client. + /// + public partial class NetworkConnection : IEquatable + { +#pragma warning disable CS0414 + #region Private. + /// + /// Last tick this connection sent a ping. + /// + private uint _lastPingTick; + /// + /// Number of times client has excessively sent a ping. + /// + private float _excessivePingCount; + /// + /// Ticks expected between each ping. + /// + private uint _requiredPingTicks; + #endregion + + #region Const. + /// + /// Number of times a ping may occur excessively before server will punish connection. + /// + private const byte EXCESSIVE_PING_LIMIT = 10; + #endregion +#pragma warning restore CS0414 + /// + /// Initializes for ping. + /// + private void InitializePing() + { + //Give the client some room for error. + float requiredInterval = (NetworkManager.TimeManager.PingInterval * 0.85f); + //Round down so required ticks is lower. + _requiredPingTicks = NetworkManager.TimeManager.TimeToTicks(requiredInterval, TickRounding.RoundDown); + } + + + /// + /// Resets PingPong values. + /// + private void ResetPingPong() + { + _excessivePingCount = 0; + _lastPingTick = 0; + } + + /// + /// Called when a ping is received from this connection. Returns if can respond to ping. + /// + /// True to respond to ping, false to kick connection. + internal bool CanPingPong() + { + /* Only check ping conditions in build. Editors are prone to pausing which can + * improperly kick clients. */ +#if UNITY_EDITOR + return true; +#else + TimeManager tm = (NetworkManager == null) ? InstanceFinder.TimeManager : NetworkManager.TimeManager; + //Server FPS is running low, timing isn't reliable enough to kick clients. + if (tm.LowFrameRate) + return true; + + uint currentTick = tm.Tick; + uint difference = (currentTick - _lastPingTick); + _lastPingTick = currentTick; + + //Ping sent too quickly. + if (difference < _requiredPingTicks) + { + _excessivePingCount += 1f; + //Ping limit hit. + if (_excessivePingCount >= EXCESSIVE_PING_LIMIT) + { + NetworkManager.LogWarning($"Kicked connectionId {ClientId} for excessive pings."); + Disconnect(true); + } + + //Return to not send pong back. + return false; + } + //Ping isnt too fast. + else + { + _excessivePingCount = UnityEngine.Mathf.Max(0f, _excessivePingCount - 0.5f); + return true; + } +#endif + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Connection/NetworkConnection.PingPong.cs.meta b/Assets/FishNet/Runtime/Connection/NetworkConnection.PingPong.cs.meta new file mode 100644 index 0000000..edf2c73 --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.PingPong.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 20633bf6995f6534ba2b27e1eab3054d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Connection/NetworkConnection.QOL.cs b/Assets/FishNet/Runtime/Connection/NetworkConnection.QOL.cs new file mode 100644 index 0000000..817b74f --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.QOL.cs @@ -0,0 +1,69 @@ +using FishNet.Managing; +using FishNet.Managing.Logging; +using FishNet.Managing.Server; +using FishNet.Serializing; +using System; + +namespace FishNet.Connection +{ + + /// + /// A container for a connected client used to perform actions on and gather information for the declared client. + /// + public partial class NetworkConnection : IEquatable + { + + #region Public. + /// + /// Returns true if this connection is a clientHost. + /// + public bool IsHost => (NetworkManager == null) ? false : (NetworkManager.IsServer && (this == NetworkManager.ClientManager.Connection)); + /// + /// Returns if this connection is for the local client. + /// + public bool IsLocalClient => (NetworkManager == null) ? false : (NetworkManager.ClientManager.Connection == this); + #endregion + + /// + /// Returns the address of this connection. + /// + /// + public string GetAddress() + { + if (!IsValid) + return string.Empty; + if (NetworkManager == null) + return string.Empty; + + return NetworkManager.TransportManager.Transport.GetConnectionAddress(ClientId); + } + + /// + /// Kicks a connection immediately while invoking OnClientKick. + /// + /// Reason client is being kicked. + /// How to print logging as. + /// Optional message to be debug logged. + public void Kick(KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "") + { + NetworkManager.ServerManager.Kick(this, kickReason, loggingType, log); + } + + + /// + /// Kicks a connection immediately while invoking OnClientKick. + /// + /// Reader to clear before kicking. + /// Reason client is being kicked. + /// How to print logging as. + /// Optional message to be debug logged. + public void Kick(Reader reader, KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "") + { + NetworkManager.ServerManager.Kick(this, reader, kickReason, loggingType, log); + } + + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Connection/NetworkConnection.QOL.cs.meta b/Assets/FishNet/Runtime/Connection/NetworkConnection.QOL.cs.meta new file mode 100644 index 0000000..b92ca8e --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.QOL.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7d45abc242399194b85e6c16bcb3676b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Connection/NetworkConnection.cs b/Assets/FishNet/Runtime/Connection/NetworkConnection.cs new file mode 100644 index 0000000..1383f80 --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.cs @@ -0,0 +1,404 @@ +using FishNet.Documenting; +using FishNet.Managing; +using FishNet.Object; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine.SceneManagement; + +namespace FishNet.Connection +{ + + /// + /// A container for a connected client used to perform actions on and gather information for the declared client. + /// + public partial class NetworkConnection : IEquatable + { + + #region Public. + /// + /// Called after this connection has loaded start scenes. Boolean will be true if asServer. Available to this connection and server. + /// + public event Action OnLoadedStartScenes; + /// + /// Called after connection gains ownership of an object, and after the object has been added to Objects. Available to this connection and server. + /// + public event Action OnObjectAdded; + /// + /// Called after connection loses ownership of an object, and after the object has been removed from Objects. Available to this connection and server. + /// + public event Action OnObjectRemoved; + /// + /// NetworkManager managing this class. + /// + public NetworkManager NetworkManager { get; private set; } + /// + /// True if connection has loaded start scenes. Available to this connection and server. + /// + public bool LoadedStartScenes() => (_loadedStartScenesAsServer || _loadedStartScenesAsClient); + /// + /// + /// + public bool LoadedStartScenes(bool asServer) + { + if (asServer) + return _loadedStartScenesAsServer; + else + return _loadedStartScenesAsClient; + } + /// + /// True if loaded start scenes as server. + /// + private bool _loadedStartScenesAsServer; + /// + /// True if loaded start scenes as client. + /// + private bool _loadedStartScenesAsClient; + /// + /// ObjectIds to use for predicted spawning. + /// + internal Queue PredictedObjectIds = new Queue(); + /// + /// True if this connection is authenticated. Only available to server. + /// + public bool Authenticated { get; private set; } + /// + /// True if this connection IsValid and not Disconnecting. + /// + public bool IsActive => (ClientId >= 0 && !Disconnecting); + /// + /// True if this connection is valid. An invalid connection indicates no client is set for this reference. + /// + public bool IsValid => (ClientId >= 0); + /// + /// Unique Id for this connection. + /// + public int ClientId = -1; + /// + /// + /// + private HashSet _objects = new HashSet(); + /// + /// Objects owned by this connection. Available to this connection and server. + /// + public IReadOnlyCollection Objects => _objects; + /// + /// The first object within Objects. + /// + public NetworkObject FirstObject { get; private set; } + /// + /// Scenes this connection is in. Available to this connection and server. + /// + public HashSet Scenes { get; private set; } = new HashSet(); + /// + /// True if this connection is being disconnected. Only available to server. + /// + public bool Disconnecting { get; private set; } + /// + /// Tick when Disconnecting was set. + /// + internal uint DisconnectingTick { get; private set; } + /// + /// Custom data associated with this connection which may be modified by the user. + /// The value of this field are not synchronized over the network. + /// + public object CustomData = null; + /// + /// Local tick when the connection last replicated. + /// + public uint LocalReplicateTick { get; internal set; } + /// + /// Tick of the last packet received from this connection. + /// This value is only available on the server. + /// + /* This is not used internally. At this time it's just + * here for the users convienence. */ + public uint LastPacketTick { get; private set; } + /// + /// Sets LastPacketTick value. + /// + /// + internal void SetLastPacketTick(uint value) + { + //If new largest tick from the client then update client tick data. + if (value > LastPacketTick) + { + _latestTick = value; + _serverLatestTick = NetworkManager.TimeManager.Tick; + } + LastPacketTick = value; + } + /// + /// Latest tick that did not arrive out of order from this connection. + /// + private uint _latestTick; + /// + /// Tick on the server when latestTick was set. + /// + private uint _serverLatestTick; + [Obsolete("Use LocalTick instead.")] //Remove on 2023/06/01 + public uint Tick => LocalTick; + /// + /// Current approximate local tick as it is on this connection. + /// + public uint LocalTick + { + get + { + NetworkManager nm = NetworkManager; + if (nm != null) + { + uint diff = (nm.TimeManager.Tick - _serverLatestTick); + return (diff + _latestTick); + } + + //Fall through, could not process. + return 0; + } + + } + #endregion + + #region Const. + /// + /// Value used when ClientId has not been set. + /// + public const int UNSET_CLIENTID_VALUE = -1; + #endregion + + #region Comparers. + public override bool Equals(object obj) + { + if (obj is NetworkConnection nc) + return (nc.ClientId == this.ClientId); + else + return false; + } + public bool Equals(NetworkConnection nc) + { + if (nc is null) + return false; + //If either is -1 Id. + if (this.ClientId == NetworkConnection.UNSET_CLIENTID_VALUE || nc.ClientId == NetworkConnection.UNSET_CLIENTID_VALUE) + return false; + //Same object. + if (System.Object.ReferenceEquals(this, nc)) + return true; + + return (this.ClientId == nc.ClientId); + } + public override int GetHashCode() + { + return ClientId; + } + public static bool operator ==(NetworkConnection a, NetworkConnection b) + { + if (a is null && b is null) + return true; + if (a is null && !(b is null)) + return false; + + return (b == null) ? a.Equals(b) : b.Equals(a); + } + public static bool operator !=(NetworkConnection a, NetworkConnection b) + { + return !(a == b); + } + #endregion + + [APIExclude] + public NetworkConnection() { } + [APIExclude] + public NetworkConnection(NetworkManager manager, int clientId, bool asServer) + { + Initialize(manager, clientId, asServer); + } + + public void Dispose() + { + foreach (PacketBundle p in _toClientBundles) + p.Dispose(); + _toClientBundles.Clear(); + } + + /// + /// Initializes this for use. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Initialize(NetworkManager nm, int clientId, bool asServer) + { + NetworkManager = nm; + ClientId = clientId; + //Only the server uses the ping and buffer. + if (asServer) + { + InitializeBuffer(); + InitializePing(); + } + } + + /// + /// Resets this instance. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void Reset() + { + _latestTick = 0; + _serverLatestTick = 0; + LastPacketTick = 0; + ClientId = -1; + ClearObjects(); + Authenticated = false; + NetworkManager = null; + _loadedStartScenesAsClient = false; + _loadedStartScenesAsServer = false; + SetDisconnecting(false); + Scenes.Clear(); + PredictedObjectIds.Clear(); + ResetPingPong(); + LevelOfDetails.Clear(); + AllowedForcedLodUpdates = 0; + LastLevelOfDetailUpdate = 0; + LevelOfDetailInfractions = 0; + } + + /// + /// Sets Disconnecting boolean for this connection. + /// + internal void SetDisconnecting(bool value) + { + Disconnecting = value; + if (Disconnecting) + DisconnectingTick = NetworkManager.TimeManager.LocalTick; + } + + /// + /// Disconnects this connection. + /// + /// True to disconnect immediately. False to send any pending data first. + public void Disconnect(bool immediately) + { + if (Disconnecting) + { + NetworkManager.LogWarning($"ClientId {ClientId} is already disconnecting."); + return; + } + + SetDisconnecting(true); + //If immediately then force disconnect through transport. + if (immediately) + NetworkManager.TransportManager.Transport.StopConnection(ClientId, true); + //Otherwise mark dirty so server will push out any pending information, and then disconnect. + else + ServerDirty(); + } + + /// + /// Returns if just loaded start scenes and sets them as loaded if not. + /// + /// + internal bool SetLoadedStartScenes(bool asServer) + { + bool loadedToCheck = (asServer) ? _loadedStartScenesAsServer : _loadedStartScenesAsClient; + //Result becomes true if not yet loaded start scenes. + bool result = !loadedToCheck; + if (asServer) + _loadedStartScenesAsServer = true; + else + _loadedStartScenesAsClient = true; + + OnLoadedStartScenes?.Invoke(this, asServer); + + return result; + } + + /// + /// Sets connection as authenticated. + /// + internal void ConnectionAuthenticated() + { + Authenticated = true; + } + + /// + /// Adds to Objects owned by this connection. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void AddObject(NetworkObject nob) + { + _objects.Add(nob); + //If adding the first object then set new FirstObject. + if (_objects.Count == 1) + FirstObject = nob; + + OnObjectAdded?.Invoke(nob); + } + + /// + /// Removes from Objects owned by this connection. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void RemoveObject(NetworkObject nob) + { + _objects.Remove(nob); + //If removing the first object then set a new one. + if (nob == FirstObject) + SetFirstObject(); + + OnObjectRemoved?.Invoke(nob); + } + + /// + /// Clears all Objects. + /// + private void ClearObjects() + { + _objects.Clear(); + FirstObject = null; + } + + /// + /// Sets FirstObject using the first element in Objects. + /// + private void SetFirstObject() + { + if (_objects.Count == 0) + { + FirstObject = null; + } + else + { + foreach (NetworkObject nob in Objects) + { + FirstObject = nob; + break; + } + } + } + + /// + /// Adds a scene to this connections Scenes. + /// + /// + /// + internal bool AddToScene(Scene scene) + { + return Scenes.Add(scene); + } + + /// + /// Removes a scene to this connections Scenes. + /// + /// + /// + internal bool RemoveFromScene(Scene scene) + { + return Scenes.Remove(scene); + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Connection/NetworkConnection.cs.meta b/Assets/FishNet/Runtime/Connection/NetworkConnection.cs.meta new file mode 100644 index 0000000..20973ee --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 01c7bfc71e29621408451fa2fa6b1a0b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Documenting.meta b/Assets/FishNet/Runtime/Documenting.meta new file mode 100644 index 0000000..213ed84 --- /dev/null +++ b/Assets/FishNet/Runtime/Documenting.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0478042f95cb685419656df549fdd04c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Documenting/Attributes.cs b/Assets/FishNet/Runtime/Documenting/Attributes.cs new file mode 100644 index 0000000..c25e901 --- /dev/null +++ b/Assets/FishNet/Runtime/Documenting/Attributes.cs @@ -0,0 +1,8 @@ + +using System; + +namespace FishNet.Documenting +{ + public class APIExcludeAttribute : Attribute { } + +} diff --git a/Assets/FishNet/Runtime/Documenting/Attributes.cs.meta b/Assets/FishNet/Runtime/Documenting/Attributes.cs.meta new file mode 100644 index 0000000..5c4e30d --- /dev/null +++ b/Assets/FishNet/Runtime/Documenting/Attributes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9d101aaaeb244ac48bfe2d7d05308c1c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor.meta b/Assets/FishNet/Runtime/Editor.meta new file mode 100644 index 0000000..4ec053b --- /dev/null +++ b/Assets/FishNet/Runtime/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 98bf936e4684b214fa9c05c45955ed0d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/CodeStripping.cs b/Assets/FishNet/Runtime/Editor/CodeStripping.cs new file mode 100644 index 0000000..3946666 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/CodeStripping.cs @@ -0,0 +1,118 @@ + +using FishNet.Configuring; +using System.IO; +using UnityEngine; +using System.Xml.Serialization; + +#if UNITY_EDITOR +using FishNet.Editing.PrefabCollectionGenerator; +using UnityEditor.Compilation; +using UnityEditor.Build.Reporting; +using UnityEditor; +using UnityEditor.Build; +#endif + +namespace FishNet.Configuring +{ + + + public class CodeStripping + + { + + /// + /// True if making a release build for client. + /// + public static bool ReleasingForClient => (Configuration.Configurations.CodeStripping.IsBuilding && !Configuration.Configurations.CodeStripping.IsHeadless && !Configuration.Configurations.CodeStripping.IsDevelopment); + /// + /// True if making a release build for server. + /// + public static bool ReleasingForServer => (Configuration.Configurations.CodeStripping.IsBuilding && Configuration.Configurations.CodeStripping.IsHeadless && !Configuration.Configurations.CodeStripping.IsDevelopment); + /// + /// Returns if to remove server logic. + /// + /// + public static bool RemoveServerLogic + { + get + { + + + /* This is to protect non pro users from enabling this + * without the extra logic code. */ +#pragma warning disable CS0162 // Unreachable code detected + return false; +#pragma warning restore CS0162 // Unreachable code detected + } + } + /// + /// True if building and stripping is enabled. + /// + public static bool StripBuild + { + get + { + + + /* This is to protect non pro users from enabling this + * without the extra logic code. */ +#pragma warning disable CS0162 // Unreachable code detected + return false; +#pragma warning restore CS0162 // Unreachable code detected + } + } + /// + /// Technique to strip methods. + /// + public static StrippingTypes StrippingType => (StrippingTypes)Configuration.Configurations.CodeStripping.StrippingType; + + private static object _compilationContext; + public int callbackOrder => 0; +#if UNITY_EDITOR + + public void OnPreprocessBuild(BuildReport report) + { + Generator.IgnorePostProcess = true; + Generator.GenerateFull(); + CompilationPipeline.compilationStarted += CompilationPipelineOnCompilationStarted; + CompilationPipeline.compilationFinished += CompilationPipelineOnCompilationFinished; + + + } + /* Solution for builds ending with errors and not triggering OnPostprocessBuild. + * Link: https://gamedev.stackexchange.com/questions/181611/custom-build-failure-callback + */ + private void CompilationPipelineOnCompilationStarted(object compilationContext) + { + _compilationContext = compilationContext; + } + + private void CompilationPipelineOnCompilationFinished(object compilationContext) + { + if (compilationContext != _compilationContext) + return; + + _compilationContext = null; + + CompilationPipeline.compilationStarted -= CompilationPipelineOnCompilationStarted; + CompilationPipeline.compilationFinished -= CompilationPipelineOnCompilationFinished; + + BuildingEnded(); + } + + private void BuildingEnded() + { + + + Generator.IgnorePostProcess = false; + } + + public void OnPostprocessBuild(BuildReport report) + { + + BuildingEnded(); + } +#endif + } + +} diff --git a/Assets/FishNet/Runtime/Editor/CodeStripping.cs.meta b/Assets/FishNet/Runtime/Editor/CodeStripping.cs.meta new file mode 100644 index 0000000..dec4f86 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/CodeStripping.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0c7409efe2f84e7428d5c6c97ed7d32e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/Configuring.meta b/Assets/FishNet/Runtime/Editor/Configuring.meta new file mode 100644 index 0000000..bf77916 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e789fd90a3d65864bbead2949dbdbb36 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationData.cs b/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationData.cs new file mode 100644 index 0000000..95127ee --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationData.cs @@ -0,0 +1,146 @@ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml.Serialization; + +#if UNITY_EDITOR +using UnityEditor; +#endif + +namespace FishNet.Configuring +{ + + public enum StrippingTypes : int + { + Redirect = 0, + Empty_Experimental = 1, + } + public enum SearchScopeType : int + { + EntireProject = 0, + SpecificFolders = 1, + } + + public class PrefabGeneratorConfigurations + { + public bool Enabled = true; + public bool LogToConsole = true; + public bool FullRebuild = false; + public bool SaveChanges = true; + public string DefaultPrefabObjectsPath = Path.Combine("Assets", "DefaultPrefabObjects.asset"); + public int SearchScope = (int)SearchScopeType.EntireProject; + public List ExcludedFolders = new List(); + public List IncludedFolders = new List(); + } + + public class CodeStrippingConfigurations + { + public bool IsBuilding = false; + public bool IsDevelopment = false; + public bool IsHeadless = false; + public bool StripReleaseBuilds = false; + public int StrippingType = (int)StrippingTypes.Redirect; + } + + + public class ConfigurationData + { + //Non serialized doesn't really do anything, its just for me. + [System.NonSerialized] + public bool Loaded; + + public PrefabGeneratorConfigurations PrefabGenerator = new PrefabGeneratorConfigurations(); + public CodeStrippingConfigurations CodeStripping = new CodeStrippingConfigurations(); + } + + public static class ConfigurationDataExtension + { + /// + /// Returns if a differs from b. + /// + public static bool HasChanged(this ConfigurationData a, ConfigurationData b) + { + return (a.CodeStripping.StripReleaseBuilds != b.CodeStripping.StripReleaseBuilds); + } + /// + /// Copies all values from source to target. + /// + public static void CopyTo(this ConfigurationData source, ConfigurationData target) + { + target.CodeStripping.StripReleaseBuilds = source.CodeStripping.StripReleaseBuilds; + } + + + /// + /// Writes a configuration data. + /// + public static void Write(this ConfigurationData cd, bool refreshAssetDatabase) + { + /* Why is this a thing you ask? Because Unity makes it VERY difficult to read values from + * memory during builds since on some Unity versions the building application is on a different + * processor. In result instead of using memory to read configurationdata the values + * must be written to disk then load the disk values as needed. + * + * Fortunatelly the file is extremely small and this does not occur often at all. The disk read + * will occur once per script save, and once per assembly when building. */ + try + { + string path = Configuration.GetAssetsPath(Configuration.CONFIG_FILE_NAME); + XmlSerializer serializer = new XmlSerializer(typeof(ConfigurationData)); + TextWriter writer = new StreamWriter(path); + serializer.Serialize(writer, cd); + writer.Close(); +#if UNITY_EDITOR + if (refreshAssetDatabase) + { + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } +#endif + } + catch (Exception ex) + { + throw new Exception($"An error occurred while writing ConfigurationData. Message: {ex.Message}"); + } + + } + + + /// + /// Writes a configuration data. + /// + public static void Write(this ConfigurationData cd, string path, bool refreshAssetDatabase) + { + /* Why is this a thing you ask? Because Unity makes it VERY difficult to read values from + * memory during builds since on some Unity versions the building application is on a different + * processor. In result instead of using memory to read configurationdata the values + * must be written to disk then load the disk values as needed. + * + * Fortunatelly the file is extremely small and this does not occur often at all. The disk read + * will occur once per script save, and once per assembly when building. */ + try + { + XmlSerializer serializer = new XmlSerializer(typeof(ConfigurationData)); + TextWriter writer = new StreamWriter(path); + serializer.Serialize(writer, cd); + writer.Close(); +#if UNITY_EDITOR + if (refreshAssetDatabase) + { + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } +#endif + } + catch (Exception ex) + { + throw new Exception($"An error occurred while writing ConfigurationData. Message: {ex.Message}"); + } + + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationData.cs.meta b/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationData.cs.meta new file mode 100644 index 0000000..95ea289 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4be37e1b0afd29944ad4fa0b92ed8c7e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationEditor.cs b/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationEditor.cs new file mode 100644 index 0000000..3b04bae --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationEditor.cs @@ -0,0 +1,126 @@ +#if UNITY_EDITOR +using FishNet.Editing.PrefabCollectionGenerator; +using FishNet.Object; +using FishNet.Utility.Extension; +using FishNet.Utility.Performance; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace FishNet.Editing +{ + public class ConfigurationEditor : EditorWindow + { + + [MenuItem("Fish-Networking/Configuration", false, 0)] + public static void ShowConfiguration() + { + SettingsService.OpenProjectSettings("Project/Fish-Networking/Configuration"); + } + + } + + public class RebuildSceneIdMenu : MonoBehaviour + { + /// + /// Rebuilds sceneIds for open scenes. + /// + [MenuItem("Fish-Networking/Rebuild SceneIds", false, 20)] + public static void RebuildSceneIds() + { + int generatedCount = 0; + for (int i = 0; i < SceneManager.sceneCount; i++) + { + Scene s = SceneManager.GetSceneAt(i); + + ListCache nobs; + SceneFN.GetSceneNetworkObjects(s, false, out nobs); + for (int z = 0; z < nobs.Written; z++) + { + NetworkObject nob = nobs.Collection[z]; + nob.TryCreateSceneID(); + EditorUtility.SetDirty(nob); + } + generatedCount += nobs.Written; + + ListCaches.StoreCache(nobs); + } + + Debug.Log($"Generated sceneIds for {generatedCount} objects over {SceneManager.sceneCount} scenes. Please save your open scenes."); + } + + + } + + public class RefreshDefaultPrefabsMenu : MonoBehaviour + { + /// + /// Rebuilds the DefaultPrefabsCollection file. + /// + [MenuItem("Fish-Networking/Refresh Default Prefabs", false, 22)] + public static void RebuildDefaultPrefabs() + { + Debug.Log("Refreshing default prefabs."); + Generator.GenerateFull(null, true); + } + + } + + + public class RemoveDuplicateNetworkObjectsMenu : MonoBehaviour + { + /// + /// Iterates all network object prefabs in the project and open scenes, removing NetworkObject components which exist multiple times on a single object. + /// + [MenuItem("Fish-Networking/Remove Duplicate NetworkObjects", false, 21)] + + public static void RemoveDuplicateNetworkObjects() + { + List foundNobs = new List(); + + foreach (string path in Generator.GetPrefabFiles("Assets", new HashSet(), true)) + { + NetworkObject nob = AssetDatabase.LoadAssetAtPath(path); + if (nob != null) + foundNobs.Add(nob); + } + + //Now add scene objects. + for (int i = 0; i < SceneManager.sceneCount; i++) + { + Scene s = SceneManager.GetSceneAt(i); + + ListCache nobs; + SceneFN.GetSceneNetworkObjects(s, false, out nobs); + for (int z = 0; z < nobs.Written; z++) + { + NetworkObject nob = nobs.Collection[z]; + nob.TryCreateSceneID(); + EditorUtility.SetDirty(nob); + } + for (int z = 0; z < nobs.Written; z++) + foundNobs.Add(nobs.Collection[i]); + + ListCaches.StoreCache(nobs); + } + + //Remove duplicates. + int removed = 0; + foreach (NetworkObject nob in foundNobs) + { + int count = nob.RemoveDuplicateNetworkObjects(); + if (count > 0) + removed += count; + } + + Debug.Log($"Removed {removed} duplicate NetworkObjects. Please save your open scenes and project."); + } + + } + + + + +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationEditor.cs.meta b/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationEditor.cs.meta new file mode 100644 index 0000000..1106133 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8135b3a4c31cfb74896f1e9e77059c89 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/Configuring/Configuring.cs b/Assets/FishNet/Runtime/Editor/Configuring/Configuring.cs new file mode 100644 index 0000000..a43925e --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/Configuring.cs @@ -0,0 +1,102 @@ +using System.IO; +using System.Xml.Serialization; +using UnityEngine; + +#if UNITY_EDITOR +using UnityEditor.Compilation; +using UnityEditor.Build.Reporting; +using UnityEditor; +using UnityEditor.Build; +#endif + +namespace FishNet.Configuring +{ + + + public class Configuration + { + + /// + /// + /// + private static ConfigurationData _configurations; + /// + /// ConfigurationData to use. + /// + public static ConfigurationData Configurations + { + get + { + if (_configurations == null) + _configurations = LoadConfigurationData(); + if (_configurations == null) + throw new System.Exception("Fish-Networking Configurations could not be loaded. Certain features such as code-stripping may not function."); + return _configurations; + } + private set + { + _configurations = value; + } + } + + /// + /// File name for configuration disk data. + /// + public const string CONFIG_FILE_NAME = "FishNet.Config.XML"; + + /// + /// Returns the path for the configuration file. + /// + /// + internal static string GetAssetsPath(string additional = "") + { + string a = Path.Combine(System.IO.Directory.GetCurrentDirectory(), "Assets"); + if (additional != "") + a = Path.Combine(a, additional); + return a; + } + /// + /// Returns FishNetworking ConfigurationData. + /// + /// + internal static ConfigurationData LoadConfigurationData() + { + //return new ConfigurationData(); + if (_configurations == null || !_configurations.Loaded) + { + string configPath = GetAssetsPath(CONFIG_FILE_NAME); + //string configPath = string.Empty; + //File is on disk. + if (File.Exists(configPath)) + { + FileStream fs = null; + try + { + XmlSerializer serializer = new XmlSerializer(typeof(ConfigurationData)); + fs = new FileStream(configPath, FileMode.Open, FileAccess.Read, FileShare.Read); + _configurations = (ConfigurationData)serializer.Deserialize(fs); + } + finally + { + fs?.Close(); + } + _configurations.Loaded = true; + } + else + { + //If null then make a new instance. + if (_configurations == null) + _configurations = new ConfigurationData(); + //Don't unset loaded, if its true then it should have proper info. + //_configurationData.Loaded = false; + } + } + + return _configurations; + + } + + } + + +} diff --git a/Assets/FishNet/Runtime/Editor/Configuring/Configuring.cs.meta b/Assets/FishNet/Runtime/Editor/Configuring/Configuring.cs.meta new file mode 100644 index 0000000..0d34195 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/Configuring.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8d05bf07ec9af2c46a1fe6c24871cccb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/Configuring/FIshNetGettingStartedEditor.cs b/Assets/FishNet/Runtime/Editor/Configuring/FIshNetGettingStartedEditor.cs new file mode 100644 index 0000000..9b24f81 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/FIshNetGettingStartedEditor.cs @@ -0,0 +1,172 @@ +#if UNITY_EDITOR +using UnityEditor; +using UnityEngine; + +namespace FishNet.Editing +{ + + /// + /// Contributed by YarnCat! Thank you! + /// + public class FishNetGettingStartedEditor : EditorWindow + { + private Texture2D _fishnetLogo, _reviewButtonBg, _reviewButtonBgHover; + private GUIStyle _labelStyle, _reviewButtonStyle; + + private const string SHOWED_GETTING_STARTED = "ShowedFishNetGettingStarted"; + + [MenuItem("Fish-Networking/Getting Started")] + public static void GettingStartedMenu() + { + FishNetGettingStartedEditor window = (FishNetGettingStartedEditor)EditorWindow.GetWindow(typeof(FishNetGettingStartedEditor)); + window.position = new Rect(0, 0, 320, 355); + Rect mainPos; +#if UNITY_2020_1_OR_NEWER + mainPos = EditorGUIUtility.GetMainWindowPosition(); +#else + mainPos = new Rect(Vector2.zero, Vector2.zero); +#endif + var pos = window.position; + float w = (mainPos.width - pos.width) * 0.5f; + float h = (mainPos.height - pos.height) * 0.5f; + pos.x = mainPos.x + w; + pos.y = mainPos.y + h; + window.position = pos; + + window._fishnetLogo = (Texture2D)AssetDatabase.LoadAssetAtPath("Assets/FishNet/Runtime/Editor/Textures/UI/Logo_With_Text.png", typeof(Texture)); + window._labelStyle = new GUIStyle("label"); + window._labelStyle.fontSize = 24; + window._labelStyle.wordWrap = true; + //window.labelStyle.alignment = TextAnchor.MiddleCenter; + window._labelStyle.normal.textColor = new Color32(74, 195, 255, 255); + + window._reviewButtonBg = MakeBackgroundTexture(1, 1, new Color32(52, 111, 255, 255)); + window._reviewButtonBgHover = MakeBackgroundTexture(1, 1, new Color32(99, 153, 255, 255)); + window._reviewButtonStyle = new GUIStyle("button"); + window._reviewButtonStyle.fontSize = 18; + window._reviewButtonStyle.fontStyle = FontStyle.Bold; + window._reviewButtonStyle.normal.background = window._reviewButtonBg; + window._reviewButtonStyle.active.background = window._reviewButtonBgHover; + window._reviewButtonStyle.focused.background = window._reviewButtonBgHover; + window._reviewButtonStyle.onFocused.background = window._reviewButtonBgHover; + window._reviewButtonStyle.hover.background = window._reviewButtonBgHover; + window._reviewButtonStyle.onHover.background = window._reviewButtonBgHover; + window._reviewButtonStyle.alignment = TextAnchor.MiddleCenter; + window._reviewButtonStyle.normal.textColor = new Color(1, 1, 1, 1); + + } + + + private static bool _subscribed; + + [InitializeOnLoadMethod] + private static void Initialize() + { + SubscribeToUpdate(); + } + + private static void SubscribeToUpdate() + { + if (Application.isBatchMode) + return; + + if (!_subscribed && !EditorApplication.isPlayingOrWillChangePlaymode) + { + _subscribed = true; + EditorApplication.update += ShowGettingStarted; + } + } + + private static void ShowGettingStarted() + { + EditorApplication.update -= ShowGettingStarted; + + bool shown = EditorPrefs.GetBool(SHOWED_GETTING_STARTED, false); + if (!shown) + { + EditorPrefs.SetBool(SHOWED_GETTING_STARTED, true); + ReviewReminderEditor.ResetDateTimeReminded(); + GettingStartedMenu(); + } + //If was already shown then check review reminder instead. + else + { + ReviewReminderEditor.CheckRemindToReview(); + } + } + + + void OnGUI() + { + + + GUILayout.Box(_fishnetLogo, GUILayout.Width(this.position.width), GUILayout.Height(128)); + GUILayout.Space(20); + + GUILayout.Label("Have you considered leaving us a review?", _labelStyle, GUILayout.Width(280)); + + GUILayout.Space(10); + + if (GUILayout.Button("Leave us a review!", _reviewButtonStyle)) + { + Application.OpenURL("https://assetstore.unity.com/packages/tools/network/fish-net-networking-evolved-207815"); + } + + GUILayout.Space(20); + + EditorGUILayout.BeginHorizontal(); + if (GUILayout.Button("Documentation", GUILayout.Width(this.position.width * 0.485f))) + { + Application.OpenURL("https://fish-networking.gitbook.io/docs/"); + } + + if (GUILayout.Button("Discord", GUILayout.Width(this.position.width * 0.485f))) + { + Application.OpenURL("https://discord.gg/Ta9HgDh4Hj"); + } + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + if (GUILayout.Button("FishNet Pro", GUILayout.Width(this.position.width * 0.485f))) + { + Application.OpenURL("https://fish-networking.gitbook.io/docs/master/pro"); + } + + if (GUILayout.Button("Github", GUILayout.Width(this.position.width * 0.485f))) + { + Application.OpenURL("https://github.com/FirstGearGames/FishNet"); + } + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + if (GUILayout.Button("Pro Downloads", GUILayout.Width(this.position.width * 0.485f))) + { + Application.OpenURL("https://www.firstgeargames.com/"); + } + + if (GUILayout.Button("Examples", GUILayout.Width(this.position.width * 0.485f))) + { + Application.OpenURL("https://fish-networking.gitbook.io/docs/manual/tutorials/example-projects"); + } + EditorGUILayout.EndHorizontal(); + + //GUILayout.Space(20); + //_showOnStartupSelected = EditorGUILayout.Popup("Show on Startup", _showOnStartupSelected, showOnStartupOptions); + } + //private string[] showOnStartupOptions = new string[] { "Always", "On new version", "Never", }; + //private int _showOnStartupSelected = 1; + + private static Texture2D MakeBackgroundTexture(int width, int height, Color color) + { + Color[] pixels = new Color[width * height]; + for (int i = 0; i < pixels.Length; i++) + pixels[i] = color; + Texture2D backgroundTexture = new Texture2D(width, height); + backgroundTexture.SetPixels(pixels); + backgroundTexture.Apply(); + return backgroundTexture; + } + } + +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Editor/Configuring/FIshNetGettingStartedEditor.cs.meta b/Assets/FishNet/Runtime/Editor/Configuring/FIshNetGettingStartedEditor.cs.meta new file mode 100644 index 0000000..0ce3c87 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/FIshNetGettingStartedEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 335aec9a9dce4944994cb57ac704ba5a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/Configuring/ReviewReminderEditor.cs b/Assets/FishNet/Runtime/Editor/Configuring/ReviewReminderEditor.cs new file mode 100644 index 0000000..70028b8 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/ReviewReminderEditor.cs @@ -0,0 +1,171 @@ +#if UNITY_EDITOR +using System; +using UnityEditor; +using UnityEngine; + +namespace FishNet.Editing +{ + + /// + /// Contributed by YarnCat! Thank you! + /// + public class ReviewReminderEditor : EditorWindow + { + private Texture2D _fishnetLogo, _reviewButtonBg, _reviewButtonBgHover; + private GUIStyle _labelStyle, _reviewButtonStyle; + + private const string DATETIME_REMINDED = "ReviewDateTimeReminded"; + private const string CHECK_REMIND_COUNT = "CheckRemindCount"; + private const string IS_ENABLED = "ReminderEnabled"; + + private static ReviewReminderEditor _window; + + internal static void CheckRemindToReview() + { + bool reminderEnabled = EditorPrefs.GetBool(IS_ENABLED, true); + if (!reminderEnabled) + return; + + /* Require at least two opens and 10 days + * to be passed before reminding. */ + int checkRemindCount = (EditorPrefs.GetInt(CHECK_REMIND_COUNT, 0) + 1); + EditorPrefs.SetInt(CHECK_REMIND_COUNT, checkRemindCount); + + //Not enough checks. + if (checkRemindCount < 2) + return; + + string dtStr = EditorPrefs.GetString(DATETIME_REMINDED, string.Empty); + //Somehow got cleared. Reset. + if (string.IsNullOrWhiteSpace(dtStr)) + { + ResetDateTimeReminded(); + return; + } + long binary; + //Failed to parse. + if (!long.TryParse(dtStr, out binary)) + { + ResetDateTimeReminded(); + return; + } + //Not enough time passed. + DateTime dt = DateTime.FromBinary(binary); + if ((DateTime.Now - dt).TotalDays < 10) + return; + + //If here then the reminder can be shown. + EditorPrefs.SetInt(CHECK_REMIND_COUNT, 0); + + ShowReminder(); + } + + internal static void ResetDateTimeReminded() + { + EditorPrefs.SetString(DATETIME_REMINDED, DateTime.Now.ToBinary().ToString()); + } + + private static void ShowReminder() + { + InitializeWindow(); + } + + static void InitializeWindow() + { + if (_window != null) + return; + _window = (ReviewReminderEditor)EditorWindow.GetWindow(typeof(ReviewReminderEditor)); + _window.position = new Rect(0f, 0f, 320f, 300f); + Rect mainPos; +#if UNITY_2020_1_OR_NEWER + mainPos = EditorGUIUtility.GetMainWindowPosition(); +#else + mainPos = new Rect(Vector2.zero, Vector2.zero); +#endif + var pos = _window.position; + float w = (mainPos.width - pos.width) * 0.5f; + float h = (mainPos.height - pos.height) * 0.5f; + pos.x = mainPos.x + w; + pos.y = mainPos.y + h; + _window.position = pos; + } + + static void StyleWindow() + { + InitializeWindow(); + _window._fishnetLogo = (Texture2D)AssetDatabase.LoadAssetAtPath("Assets/FishNet/Runtime/Editor/Textures/UI/Logo_With_Text.png", typeof(Texture)); + _window._labelStyle = new GUIStyle("label"); + _window._labelStyle.fontSize = 24; + _window._labelStyle.wordWrap = true; + //window.labelStyle.alignment = TextAnchor.MiddleCenter; + _window._labelStyle.normal.textColor = new Color32(74, 195, 255, 255); + + _window._reviewButtonBg = MakeBackgroundTexture(1, 1, new Color32(52, 111, 255, 255)); + _window._reviewButtonBgHover = MakeBackgroundTexture(1, 1, new Color32(99, 153, 255, 255)); + _window._reviewButtonStyle = new GUIStyle("button"); + _window._reviewButtonStyle.fontSize = 18; + _window._reviewButtonStyle.fontStyle = FontStyle.Bold; + _window._reviewButtonStyle.normal.background = _window._reviewButtonBg; + _window._reviewButtonStyle.active.background = _window._reviewButtonBgHover; + _window._reviewButtonStyle.focused.background = _window._reviewButtonBgHover; + _window._reviewButtonStyle.onFocused.background = _window._reviewButtonBgHover; + _window._reviewButtonStyle.hover.background = _window._reviewButtonBgHover; + _window._reviewButtonStyle.onHover.background = _window._reviewButtonBgHover; + _window._reviewButtonStyle.alignment = TextAnchor.MiddleCenter; + _window._reviewButtonStyle.normal.textColor = new Color(1, 1, 1, 1); + + } + + void OnGUI() + { + float thisWidth = this.position.width; + StyleWindow(); + GUILayout.Box(_fishnetLogo, GUILayout.Width(this.position.width), GUILayout.Height(160f)); + EditorGUILayout.BeginHorizontal(); + GUILayout.Space(8f); + GUILayout.Label("Have you considered leaving us a review?", _labelStyle, GUILayout.Width(thisWidth * 0.95f)); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + if (GUILayout.Button("Don't Ask Again", GUILayout.Width(this.position.width))) + { + this.Close(); + EditorPrefs.SetBool(IS_ENABLED, false); + } + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + if (GUILayout.Button("Ask Later", GUILayout.Width(this.position.width))) + { + this.Close(); + Application.OpenURL("https://discord.gg/Ta9HgDh4Hj"); + } + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + if (GUILayout.Button("Leave A Review", GUILayout.Width(this.position.width))) + { + this.Close(); + EditorPrefs.SetBool(IS_ENABLED, false); + Application.OpenURL("https://assetstore.unity.com/packages/tools/network/fish-net-networking-evolved-207815"); + } + EditorGUILayout.EndHorizontal(); + + //GUILayout.Space(20); + //_showOnStartupSelected = EditorGUILayout.Popup("Show on Startup", _showOnStartupSelected, showOnStartupOptions); + } + + private static Texture2D MakeBackgroundTexture(int width, int height, Color color) + { + Color[] pixels = new Color[width * height]; + for (int i = 0; i < pixels.Length; i++) + pixels[i] = color; + Texture2D backgroundTexture = new Texture2D(width, height); + backgroundTexture.SetPixels(pixels); + backgroundTexture.Apply(); + return backgroundTexture; + } + } + +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Editor/Configuring/ReviewReminderEditor.cs.meta b/Assets/FishNet/Runtime/Editor/Configuring/ReviewReminderEditor.cs.meta new file mode 100644 index 0000000..cb27f6b --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/ReviewReminderEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4260206b6a57e4243b56437f8f283084 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/Configuring/SettingsProvider.cs b/Assets/FishNet/Runtime/Editor/Configuring/SettingsProvider.cs new file mode 100644 index 0000000..ec66b2a --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/SettingsProvider.cs @@ -0,0 +1,86 @@ +#if UNITY_EDITOR + +using UnityEngine; +using UnityEditor; +using FishNet.Configuring; + +using UnitySettingsProviderAttribute = UnityEditor.SettingsProviderAttribute; +using UnitySettingsProvider = UnityEditor.SettingsProvider; +using System.Collections.Generic; + +namespace FishNet.Configuring.Editing +{ + internal static class SettingsProvider + { + private static Vector2 _scrollView; + + [UnitySettingsProvider] + private static UnitySettingsProvider Create() + { + return new UnitySettingsProvider("Project/Fish-Networking/Configuration", SettingsScope.Project) + { + label = "Configuration", + + guiHandler = OnGUI, + + keywords = new string[] + { + "Fish", + "Networking", + "Configuration", + }, + }; + } + + private static void OnGUI(string searchContext) + { + ConfigurationData configuration = Configuration.LoadConfigurationData(); + + if (configuration == null) + { + EditorGUILayout.HelpBox("Unable to load configuration data.", MessageType.Error); + + return; + } + + EditorGUI.BeginChangeCheck(); + + GUIStyle scrollViewStyle = new GUIStyle() + { + padding = new RectOffset(10, 10, 10, 10), + }; + + _scrollView = GUILayout.BeginScrollView(_scrollView, scrollViewStyle); + + EditorGUILayout.BeginHorizontal(); + + GUIStyle toggleStyle = new GUIStyle(EditorStyles.toggle) + { + richText = true, + }; + + configuration.CodeStripping.StripReleaseBuilds = GUILayout.Toggle(configuration.CodeStripping.StripReleaseBuilds, $"{ObjectNames.NicifyVariableName(nameof(configuration.CodeStripping.StripReleaseBuilds))} (Pro Only)", toggleStyle); + + EditorGUILayout.EndHorizontal(); + + if (configuration.CodeStripping.StripReleaseBuilds) + { + EditorGUI.indentLevel++; + //Stripping Method. + List enumStrings = new List(); + foreach (string item in System.Enum.GetNames(typeof(StrippingTypes))) + enumStrings.Add(item); + configuration.CodeStripping.StrippingType = EditorGUILayout.Popup($"{ObjectNames.NicifyVariableName(nameof(configuration.CodeStripping.StrippingType))}", (int)configuration.CodeStripping.StrippingType, enumStrings.ToArray()); + + EditorGUILayout.HelpBox("Development builds will not have code stripped. Additionally, if you plan to run as host disable code stripping.", MessageType.Warning); + EditorGUI.indentLevel--; + } + + GUILayout.EndScrollView(); + + if (EditorGUI.EndChangeCheck()) Configuration.Configurations.Write(true); + } + } +} + +#endif diff --git a/Assets/FishNet/Runtime/Editor/Configuring/SettingsProvider.cs.meta b/Assets/FishNet/Runtime/Editor/Configuring/SettingsProvider.cs.meta new file mode 100644 index 0000000..d668951 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/SettingsProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b3d7d3c45d53dea4e8a0a7da73d64021 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/Constants.cs b/Assets/FishNet/Runtime/Editor/Constants.cs new file mode 100644 index 0000000..1e8523d --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Constants.cs @@ -0,0 +1,13 @@ +using FishNet.Documenting; + +namespace FishNet.Editing +{ + [APIExclude] + public static class EditingConstants + { + public const string PRO_ASSETS_LOCKED_TEXT = "Fields marked with * are only active with Fish-Networking Pro."; + public const string PRO_ASSETS_UNLOCKED_TEXT = "Thank you for supporting Fish-Networking! Pro asset features are unlocked."; + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Editor/Constants.cs.meta b/Assets/FishNet/Runtime/Editor/Constants.cs.meta new file mode 100644 index 0000000..bfee3c7 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Constants.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1d2683b3becd2c5488c1f338972d49e0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/DefaultPrefabsFinder.cs b/Assets/FishNet/Runtime/Editor/DefaultPrefabsFinder.cs new file mode 100644 index 0000000..626cabc --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/DefaultPrefabsFinder.cs @@ -0,0 +1,126 @@ +//#if UNITY_EDITOR +//using FishNet.Managing.Object; +//using FishNet.Object; +//using System.Collections.Generic; +//using System.IO; +//using UnityEditor; +//using UnityEngine; + +//namespace FishNet.Editing +//{ + +// [InitializeOnLoad] +// internal static class DefaultPrefabsFinder +// { +// /// +// /// True if initialized. +// /// +// [System.NonSerialized] +// private static bool _initialized; +// /// +// /// Found default prefabs. +// /// +// private static DefaultPrefabObjects _defaultPrefabs; + +// static DefaultPrefabsFinder() +// { +// EditorApplication.update += InitializeOnce; +// } + +// /// +// /// Finds and sets the default prefabs reference. +// /// +// internal static DefaultPrefabObjects GetDefaultPrefabsFile(out bool justPopulated) +// { +// if (_defaultPrefabs == null) +// { +// List results = Finding.GetScriptableObjects(true, true); +// if (results.Count > 0) +// _defaultPrefabs = (DefaultPrefabObjects)results[0]; +// } + +// justPopulated = false; +// //If not found then try to create file. +// if (_defaultPrefabs == null) +// { +// if (DefaultPrefabObjects.CanAutomate) +// { +// DefaultPrefabObjects dpo = ScriptableObject.CreateInstance(); +// //Get save directory. +// string savePath = Finding.GetFishNetRuntimePath(true); +// AssetDatabase.CreateAsset(dpo, Path.Combine(savePath, $"{nameof(DefaultPrefabObjects)}.asset")); +// } +// else +// { +// Debug.LogError($"Cannot create DefaultPrefabs because auto create is blocked."); +// } +// } + +// //If still null. +// if (_defaultPrefabs == null) +// Debug.LogWarning($"DefaultPrefabObjects not found. Prefabs list will not be automatically populated."); +// else +// justPopulated = PopulateDefaultPrefabs(); + +// return _defaultPrefabs; +// } + +// /// +// /// Initializes the default prefab. +// /// +// private static void InitializeOnce() +// { +// if (_initialized) +// return; +// _initialized = true; + +// Finding.GetFishNetRuntimePath(false); +// GetDefaultPrefabsFile(out _); + +// if (_defaultPrefabs != null) +// { +// //Populate any missing. +// if (_defaultPrefabs.GetObjectCount() == 0) +// PopulateDefaultPrefabs(); +// } +// } + + +// /// +// /// Finds all NetworkObjects in project and adds them to defaultPrefabs. +// /// +// /// True if was populated from assets. +// internal static bool PopulateDefaultPrefabs(bool log = true, bool clear = false) +// { +// if (_defaultPrefabs == null) +// return false; +// if (!DefaultPrefabObjects.CanAutomate) +// return false; +// if (clear) +// _defaultPrefabs.Clear(); +// if (_defaultPrefabs.GetObjectCount() > 0) +// return false; + +// List gameObjects = Finding.GetGameObjects(true, true, false); +// foreach (GameObject go in gameObjects) +// { +// if (go.TryGetComponent(out NetworkObject nob)) +// _defaultPrefabs.AddObject(nob); +// } + +// _defaultPrefabs.Sort(); + +// int entriesAdded = _defaultPrefabs.GetObjectCount(); +// //Only print if some were added. +// if (log && entriesAdded > 0) +// Debug.Log($"Default prefabs was populated with {entriesAdded} prefabs."); + +// EditorUtility.SetDirty(_defaultPrefabs); +// return true; +// } + +// } + + +//} +//#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Editor/DefaultPrefabsFinder.cs.meta b/Assets/FishNet/Runtime/Editor/DefaultPrefabsFinder.cs.meta new file mode 100644 index 0000000..1ee03df --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/DefaultPrefabsFinder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2bd002f6c85dd4341bcaf163eaaa3ddf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/Finding.cs b/Assets/FishNet/Runtime/Editor/Finding.cs new file mode 100644 index 0000000..2d260c8 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Finding.cs @@ -0,0 +1,216 @@ +#if UNITY_EDITOR +using FishNet.Utility.Constant; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace FishNet.Editing +{ + public static class Finding + { + #region Private. + /// + /// Path where the FishNet.Runtime assembly is. + /// + [System.NonSerialized] + private static string _fishNetRuntimePath = string.Empty; + /// + /// Path where the FishNet.Generated assembly is. + /// + private static string _fishNetGeneratedPath = string.Empty; + #endregion + + /// + /// Sets FishNet assembly paths. + /// + /// + private static void SetPaths(bool error) + { + if (_fishNetGeneratedPath != string.Empty && _fishNetRuntimePath != string.Empty) + return; + + string[] guids = AssetDatabase.FindAssets("t:asmdef", new string[] { "Assets" }); + string[] objectPaths = new string[guids.Length]; + for (int i = 0; i < guids.Length; i++) + objectPaths[i] = AssetDatabase.GUIDToAssetPath(guids[i]); + + string runtimeName = (UtilityConstants.RUNTIME_ASSEMBLY_NAME + ".asmdef").ToLower(); + string generatedName = (UtilityConstants.GENERATED_ASSEMBLY_NAME + ".asmdef").ToLower(); + /* Find all network managers which use Single prefab linking + * as well all network object prefabs. */ + foreach (string item in objectPaths) + { + //Found directory to create object in. + if (item.ToLower().Contains(runtimeName)) + _fishNetRuntimePath = System.IO.Path.GetDirectoryName(item); + else if (item.ToLower().Contains(generatedName)) + _fishNetGeneratedPath = System.IO.Path.GetDirectoryName(item); + + if (_fishNetGeneratedPath != string.Empty && _fishNetRuntimePath != string.Empty) + return; + } + } + /// + /// Gets path for where the FishNet.Runtime assembly is. + /// + /// + public static string GetFishNetRuntimePath(bool error) + { + SetPaths(error); + return _fishNetRuntimePath; + } + /// + /// Gets path for where the FishNet.Generated assembly is. + /// + /// + /// + public static string GetFishNetGeneratedPath(bool error) + { + SetPaths(error); + return _fishNetGeneratedPath; + } + + /// + /// Gets all GameObjects in Assets and optionally scenes. + /// + /// + /// + public static List GetGameObjects(bool userAssemblies, bool fishNetAssembly, bool includeScenes, string[] ignoredPaths = null) + { + List results = new List(); + + string[] guids; + string[] objectPaths; + + guids = AssetDatabase.FindAssets("t:GameObject", null); + objectPaths = new string[guids.Length]; + for (int i = 0; i < guids.Length; i++) + objectPaths[i] = AssetDatabase.GUIDToAssetPath(guids[i]); + + foreach (string item in objectPaths) + { + bool inFishNet = item.Contains(_fishNetRuntimePath); + if (inFishNet && !fishNetAssembly) + continue; + if (!inFishNet && !userAssemblies) + continue; + if (ignoredPaths != null) + { + bool ignore = false; + foreach (string path in ignoredPaths) + { + if (item.Contains(path)) + { + ignore = true; + break; + } + } + if (ignore) + continue; + } + + GameObject go = (GameObject)AssetDatabase.LoadAssetAtPath(item, typeof(GameObject)); + results.Add(go); + } + + if (includeScenes) + results.AddRange(GetSceneGameObjects()); + + return results; + } + + /// + /// Gets all GameObjects in all open scenes. + /// + /// + private static List GetSceneGameObjects() + { + List results = new List(); + + for (int i = 0; i < SceneManager.sceneCount; i++) + results.AddRange(GetSceneGameObjects(SceneManager.GetSceneAt(i))); + + return results; + } + /// + /// Gets all GameObjects in a scene. + /// + private static List GetSceneGameObjects(Scene s) + { + List results = new List(); + List buffer = new List(); + //Iterate all root objects for the scene. + GameObject[] gos = s.GetRootGameObjects(); + for (int i = 0; i < gos.Length; i++) + { + /* Get GameObjects within children of each + * root object then add them to the cache. */ + gos[i].GetComponentsInChildren(true, buffer); + foreach (Transform t in buffer) + results.Add(t.gameObject); + } + + return results; + } + + + /// + /// Gets created ScriptableObjects of T. + /// + /// + /// + public static List GetScriptableObjects(bool fishNetAssembly, bool breakOnFirst = false) + { + System.Type tType = typeof(T); + List results = new List(); + + string[] guids = AssetDatabase.FindAssets("t:ScriptableObject", new string[] { "Assets" }); + string[] objectPaths = new string[guids.Length]; + for (int i = 0; i < guids.Length; i++) + objectPaths[i] = AssetDatabase.GUIDToAssetPath(guids[i]); + + + /* This might be faster than using directory comparers. + * Don't really care since this occurs only at edit. */ + List fishNetPaths = new List(); + fishNetPaths.Add(_fishNetGeneratedPath.Replace(@"/", @"\")); + fishNetPaths.Add(_fishNetGeneratedPath.Replace(@"\", @"/")); + fishNetPaths.Add(_fishNetRuntimePath.Replace(@"/", @"\")); + fishNetPaths.Add(_fishNetRuntimePath.Replace(@"\", @"/")); + /* Find all network managers which use Single prefab linking + * as well all network object prefabs. */ + foreach (string item in objectPaths) + { + //This will skip hidden unity types. + if (!item.EndsWith(".asset")) + continue; + if (fishNetAssembly) + { + bool found = false; + foreach (string path in fishNetPaths) + { + if (item.Contains(path)) + { + found = true; + break; + } + } + if (!found) + continue; + } + UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath(item, tType); + if (obj != null && tType != null && obj.GetType() == tType) + { + results.Add(obj); + if (breakOnFirst) + return results; + } + } + + return results; + } + + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Editor/Finding.cs.meta b/Assets/FishNet/Runtime/Editor/Finding.cs.meta new file mode 100644 index 0000000..23a2fb5 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Finding.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b777233d19062274f9eec6a982d8ff37 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/PlayModeTracker.cs b/Assets/FishNet/Runtime/Editor/PlayModeTracker.cs new file mode 100644 index 0000000..45f7b7c --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/PlayModeTracker.cs @@ -0,0 +1,51 @@ +#if UNITY_EDITOR +using System; +using UnityEditor; +using UnityEngine; + +namespace FishNet.Editing +{ + + [InitializeOnLoad] + public class PlayModeTracker + { + static PlayModeTracker() + { + EditorApplication.playModeStateChanged += OnPlayModeStateChanged; + } + + ~PlayModeTracker() + { + EditorApplication.playModeStateChanged -= OnPlayModeStateChanged; + } + + /// + /// DateTime when the editor last exited playmode. + /// + private static DateTime _quitTime = DateTime.MaxValue; + + /// + /// True if the editor has exited playmode within past. + /// + /// + /// + internal static bool QuitRecently(float past) + { + past *= 1000; + return ((DateTime.Now - _quitTime).TotalMilliseconds < past); + } + + private static void OnPlayModeStateChanged(PlayModeStateChange stateChange) + { + switch (stateChange) + { + case (PlayModeStateChange.ExitingPlayMode): + _quitTime = DateTime.Now; + break; + } + } + + } +} + +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Editor/PlayModeTracker.cs.meta b/Assets/FishNet/Runtime/Editor/PlayModeTracker.cs.meta new file mode 100644 index 0000000..d5fd5a2 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/PlayModeTracker.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d4a1d20c6a03a524ab21c7aebed106d0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator.meta b/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator.meta new file mode 100644 index 0000000..873aa1e --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3da51acfc7419a544887f9eab923b0a7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/Generator.cs b/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/Generator.cs new file mode 100644 index 0000000..fbce389 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/Generator.cs @@ -0,0 +1,619 @@ +#if UNITY_EDITOR + +using FishNet.Configuring; +using FishNet.Managing.Object; +using FishNet.Object; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityDebug = UnityEngine.Debug; + +namespace FishNet.Editing.PrefabCollectionGenerator +{ + internal sealed class Generator : AssetPostprocessor + { + public Generator() + { + if (!_subscribed) + { + _subscribed = true; + EditorApplication.update += OnEditorUpdate; + } + } + ~Generator() + { + if (_subscribed) + { + _subscribed = false; + EditorApplication.update -= OnEditorUpdate; + } + } + + #region Types. + private struct SpecifiedFolder + { + public string Path; + public bool Recursive; + + public SpecifiedFolder(string path, bool recursive) + { + Path = path; + Recursive = recursive; + } + } + #endregion + + #region Public. + /// + /// True to ignore post process changes. + /// + public static bool IgnorePostProcess = false; + #endregion + + #region Private. + /// + /// Last asset to import when there was only one imported asset and no other changes. + /// + private static string _lastSingleImportedAsset = string.Empty; + /// + /// Cached DefaultPrefabObjects reference. + /// + private static DefaultPrefabObjects _cachedDefaultPrefabs; + /// + /// True to refresh prefabs next update. + /// + private static bool _retryRefreshDefaultPrefabs; + /// + /// True if already subscribed to EditorApplication.Update. + /// + private static bool _subscribed; + /// + /// True if ran once since editor started. + /// + [System.NonSerialized] + private static bool _ranOnce; + /// + /// Last paths of updated nobs during a changed update. + /// + [System.NonSerialized] + private static List _lastUpdatedNamePaths = new List(); + /// + /// Last frame changed was updated. + /// + [System.NonSerialized] + private static int _lastUpdatedFrame = -1; + /// + /// Length of assets strings during the last update. + /// + [System.NonSerialized] + private static int _lastUpdatedLengths = -1; + #endregion + + public static string[] GetPrefabFiles(string startingPath, HashSet excludedPaths, bool recursive) + { + //Opportunity to exit early if there are no excluded paths. + if (excludedPaths.Count == 0) + { + string[] strResults = Directory.GetFiles(startingPath, "*.prefab", SearchOption.AllDirectories); + return strResults; + } + //starting path is excluded. + if (excludedPaths.Contains(startingPath)) + return new string[0]; + + //Folders remaining to be iterated. + List enumeratedCollection = new List() { startingPath }; + //Only check other directories if recursive. + if (recursive) + { + //Find all folders which aren't excluded. + for (int i = 0; i < enumeratedCollection.Count; i++) + { + string[] allFolders = Directory.GetDirectories(enumeratedCollection[i], "*", SearchOption.TopDirectoryOnly); + for (int z = 0; z < allFolders.Length; z++) + { + string current = allFolders[z]; + //Not excluded. + if (!excludedPaths.Contains(current)) + enumeratedCollection.Add(current); + } + } + } + + //Valid prefab files. + List results = new List(); + //Build files from folders. + int count = enumeratedCollection.Count; + for (int i = 0; i < count; i++) + { + string[] r = Directory.GetFiles(enumeratedCollection[i], "*.prefab", SearchOption.TopDirectoryOnly); + results.AddRange(r); + } + + return results.ToArray(); + } + + /// + /// Removes paths which may overlap each other, such as sub directories. + /// + private static void RemoveOverlappingFolders(List folders) + { + for (int z = 0; z < folders.Count; z++) + { + for (int i = 0; i < folders.Count; i++) + { + //Do not check against self. + if (i == z) + continue; + + //Duplicate. + if (folders[z].Path.Equals(folders[i].Path, System.StringComparison.OrdinalIgnoreCase)) + { + UnityDebug.LogError($"The same path is specified multiple times in the DefaultPrefabGenerator settings. Remove the duplicate to clear this error."); + folders.RemoveAt(i); + break; + } + + /* We are checking if i can be within + * z. This is only possible if i is longer + * than z. */ + if (folders[i].Path.Length < folders[z].Path.Length) + continue; + /* Do not need to check if not recursive. + * Only recursive needs to be checked because + * a shorter recursive path could contain + * a longer path. */ + if (!folders[z].Recursive) + continue; + + //Compare paths. + string zPath = GetPathWithSeparator(folders[z].Path); + string iPath = zPath.Substring(0, zPath.Length); + //If paths match. + if (iPath.Equals(zPath, System.StringComparison.OrdinalIgnoreCase)) + { + UnityDebug.LogError($"Path {folders[i].Path} is included within recursive path {folders[z].Path}. Remove path {folders[i].Path} to clear this error."); + folders.RemoveAt(i); + break; + } + } + } + + string GetPathWithSeparator(string txt) + { + return txt.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + + Path.DirectorySeparatorChar; + } + } + + /// + /// Returns a message to attach to logs if objects were dirtied. + /// + private static string GetDirtiedMessage(PrefabGeneratorConfigurations settings, bool dirtied) + { + if (!settings.SaveChanges && dirtied) + return " One or more NetworkObjects were dirtied. Please save your project."; + else + return string.Empty; + } + + /// + /// Updates prefabs by using only changed information. + /// + public static void GenerateChanged(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths, PrefabGeneratorConfigurations settings = null) + { + if (settings == null) + settings = Configuration.Configurations.PrefabGenerator; + if (!settings.Enabled) + return; + + bool log = settings.LogToConsole; + Stopwatch sw = (log) ? Stopwatch.StartNew() : null; + + DefaultPrefabObjects prefabCollection = GetDefaultPrefabObjects(settings); + //No need to error if nto found, GetDefaultPrefabObjects will. + if (prefabCollection == null) + return; + + int assetsLength = (importedAssets.Length + deletedAssets.Length + movedAssets.Length + movedFromAssetPaths.Length); + List changedNobPaths = new List(); + + System.Type goType = typeof(UnityEngine.GameObject); + IterateAssetCollection(importedAssets); + IterateAssetCollection(movedAssets); + + //True if dirtied by changes. + bool dirtied; + //First remove null entries. + int startCount = prefabCollection.GetObjectCount(); + prefabCollection.RemoveNull(); + dirtied = (prefabCollection.GetObjectCount() != startCount); + //First index which new objects will be added to. + int firstAddIndex = (prefabCollection.GetObjectCount() - 1); + + //Iterates strings adding prefabs to collection. + void IterateAssetCollection(string[] c) + { + foreach (string item in c) + { + System.Type assetType = AssetDatabase.GetMainAssetTypeAtPath(item); + if (assetType != goType) + continue; + + NetworkObject nob = AssetDatabase.LoadAssetAtPath(item); + if (nob != null) + { + changedNobPaths.Add(item); + prefabCollection.AddObject(nob, true); + dirtied = true; + } + } + } + + //To prevent out of range. + if (firstAddIndex < 0 || firstAddIndex >= prefabCollection.GetObjectCount()) + firstAddIndex = 0; + dirtied |= prefabCollection.SetAssetPathHashes(firstAddIndex); + + if (log && dirtied) + UnityDebug.Log($"Default prefab generator updated prefabs in {sw.ElapsedMilliseconds}ms.{GetDirtiedMessage(settings, dirtied)}"); + + //Check for redundancy. + int frameCount = Time.frameCount; + int changedCount = changedNobPaths.Count; + if (frameCount == _lastUpdatedFrame && assetsLength == _lastUpdatedLengths && (changedCount == _lastUpdatedNamePaths.Count) && changedCount > 0) + { + bool allMatch = true; + for (int i = 0; i < changedCount; i++) + { + if (changedNobPaths[i] != _lastUpdatedNamePaths[i]) + { + allMatch = false; + break; + } + } + + /* If the import results are the same as the last attempt, on the same frame + * then there is likely an issue saving the assets. */ + if (allMatch) + { + //Unset dirtied to prevent a save. + dirtied = false; + //Log this no matter what, it's critical. + UnityDebug.LogError($"Default prefab generator had a problem saving one or more assets. " + + $"This usually occurs when the assets cannot be saved due to missing scripts or serialization errors. " + + $"Please see above any prefabs which could not save any make corrections."); + } + + } + //Set last values. + _lastUpdatedFrame = Time.frameCount; + _lastUpdatedNamePaths = changedNobPaths; + _lastUpdatedLengths = assetsLength; + + EditorUtility.SetDirty(prefabCollection); + if (dirtied && settings.SaveChanges) + AssetDatabase.SaveAssets(); + } + + /// + /// Generates prefabs by iterating all files within settings parameters. + /// + public static void GenerateFull(PrefabGeneratorConfigurations settings = null, bool forced = false) + { + if (settings == null) + settings = Configuration.Configurations.PrefabGenerator; + if (!forced && !settings.Enabled) + return; + bool log = settings.LogToConsole; + + Stopwatch sw = (log) ? Stopwatch.StartNew() : null; + List foundNobs = new List(); + HashSet excludedPaths = new HashSet(settings.ExcludedFolders); + + //If searching the entire project. + if (settings.SearchScope == (int)SearchScopeType.EntireProject) + { + foreach (string path in GetPrefabFiles("Assets", excludedPaths, true)) + { + NetworkObject nob = AssetDatabase.LoadAssetAtPath(path); + if (nob != null) + foundNobs.Add(nob); + } + } + //Specific folders. + else if (settings.SearchScope == (int)SearchScopeType.SpecificFolders) + { + List folders = GetSpecifiedFolders(settings.IncludedFolders.ToList()); + RemoveOverlappingFolders(folders); + + foreach (SpecifiedFolder sf in folders) + { + //If specified folder doesn't exist then continue. + if (!Directory.Exists(sf.Path)) + continue; + + foreach (string path in GetPrefabFiles(sf.Path, excludedPaths, sf.Recursive)) + { + NetworkObject nob = AssetDatabase.LoadAssetAtPath(path); + if (nob != null) + foundNobs.Add(nob); + } + } + } + //Unhandled. + else + { + UnityDebug.LogError($"{settings.SearchScope} is not handled; default prefabs will not generator properly."); + } + + DefaultPrefabObjects prefabCollection = GetDefaultPrefabObjects(settings); + //No need to error if not found, GetDefaultPrefabObjects will throw. + if (prefabCollection == null) + return; + + //Clear and add built list. + prefabCollection.Clear(); + prefabCollection.AddObjects(foundNobs, false); + bool dirtied = prefabCollection.SetAssetPathHashes(0); + + int newCount = prefabCollection.GetObjectCount(); + if (log) + { + string dirtiedMessage = (newCount > 0) ? GetDirtiedMessage(settings, dirtied) : string.Empty; + UnityDebug.Log($"Default prefab generator found {newCount} prefabs in {sw.ElapsedMilliseconds}ms.{dirtiedMessage}"); + } + //Only set dirty if and save if prefabs were found. + if (newCount > 0) + { + EditorUtility.SetDirty(prefabCollection); + if (settings.SaveChanges) + AssetDatabase.SaveAssets(); + } + } + + /// + /// Iterates folders building them into SpecifiedFolders. + /// + private static List GetSpecifiedFolders(List folders) + { + List results = new List(); + //Remove astericks. + foreach (string path in folders) + { + int pLength = path.Length; + if (pLength == 0) + continue; + + bool recursive; + string p; + //If the last character indicates resursive. + if (path.Substring(pLength - 1, 1) == "*") + { + p = path.Substring(0, pLength - 1); + recursive = true; + } + else + { + p = path; + recursive = false; + } + + results.Add(new SpecifiedFolder(p, recursive)); + } + + return results; + } + + /// + /// Returns the DefaultPrefabObjects file. + /// + private static DefaultPrefabObjects GetDefaultPrefabObjects(PrefabGeneratorConfigurations settings = null) + { + if (settings == null) + settings = Configuration.Configurations.PrefabGenerator; + + //Load the prefab collection + string defaultPrefabsPath = settings.DefaultPrefabObjectsPath; + defaultPrefabsPath = defaultPrefabsPath.Replace(@"\", "/"); + string fullDefaultPrefabsPath = (defaultPrefabsPath.Length > 0) ? Path.GetFullPath(defaultPrefabsPath) : string.Empty; + + //If cached prefabs is not the same path as assetPath. + if (_cachedDefaultPrefabs != null) + { + string unityAssetPath = AssetDatabase.GetAssetPath(_cachedDefaultPrefabs); + string fullCachedPath = (unityAssetPath.Length > 0) ? Path.GetFullPath(unityAssetPath) : string.Empty; + if (fullCachedPath != fullDefaultPrefabsPath) + _cachedDefaultPrefabs = null; + } + + //If cached is null try to get it. + if (_cachedDefaultPrefabs == null) + { + //Only try to load it if file exist. + if (File.Exists(fullDefaultPrefabsPath)) + { + _cachedDefaultPrefabs = AssetDatabase.LoadAssetAtPath(defaultPrefabsPath); + if (_cachedDefaultPrefabs == null) + { + //If already retried then throw an error. + if (_retryRefreshDefaultPrefabs) + { + UnityDebug.LogError("DefaultPrefabObjects file exists but it could not be loaded by Unity. Use the Fish-Networking menu to Refresh Default Prefabs."); + } + else + { + UnityDebug.Log("DefaultPrefabObjects file exists but it could not be loaded by Unity. Trying to reload the file next frame."); + _retryRefreshDefaultPrefabs = true; + } + return null; + } + } + } + + if (_cachedDefaultPrefabs == null) + { + string fullPath = Path.GetFullPath(defaultPrefabsPath); + UnityDebug.Log($"Creating a new DefaultPrefabsObject at {fullPath}."); + string directory = Path.GetDirectoryName(fullPath); + + if (!Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + AssetDatabase.Refresh(); + } + + _cachedDefaultPrefabs = ScriptableObject.CreateInstance(); + AssetDatabase.CreateAsset(_cachedDefaultPrefabs, defaultPrefabsPath); + AssetDatabase.SaveAssets(); + } + + if (_cachedDefaultPrefabs != null && _retryRefreshDefaultPrefabs) + UnityDebug.Log("DefaultPrefabObjects found on the second iteration."); + return _cachedDefaultPrefabs; + } + + /// + /// Called every frame the editor updates. + /// + private static void OnEditorUpdate() + { + if (!_retryRefreshDefaultPrefabs) + return; + + GenerateFull(); + _retryRefreshDefaultPrefabs = false; + } + + /// + /// Called by Unity when assets are modified. + /// + private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) + { + if (Application.isPlaying) + return; + //If retrying next frame don't bother updating, next frame will do a full refresh. + if (_retryRefreshDefaultPrefabs) + return; + //Post process is being ignored. Could be temporary or user has disabled this feature. + if (IgnorePostProcess) + return; + /* Don't iterate if updating or compiling as that could cause an infinite loop + * due to the prefabs being generated during an update, which causes the update + * to start over, which causes the generator to run again, which... you get the idea. */ + if (EditorApplication.isCompiling) + return; + + DefaultPrefabObjects prefabCollection = GetDefaultPrefabObjects(); + if (prefabCollection == null) + return; + PrefabGeneratorConfigurations settings = Configuration.Configurations.PrefabGenerator; + + if (prefabCollection.GetObjectCount() == 0) + { + //If there are no prefabs then do a full rebuild. Odds of there being none are pretty much nill. + GenerateFull(settings); + } + else + { + int totalChanges = importedAssets.Length + deletedAssets.Length + movedAssets.Length + movedFromAssetPaths.Length; + //Nothing has changed. This shouldn't occur but unity is funny so we're going to check anyway. + if (totalChanges == 0) + return; + + //normalizes path. + string dpoPath = Path.GetFullPath(settings.DefaultPrefabObjectsPath); + //If total changes is 1 and the only changed file is the default prefab collection then do nothing. + if (totalChanges == 1) + { + //Do not need to check movedFromAssetPaths because that's not possible for this check. + if ((importedAssets.Length == 1 && Path.GetFullPath(importedAssets[0]) == dpoPath) + || (deletedAssets.Length == 1 && Path.GetFullPath(deletedAssets[0]) == dpoPath) + || (movedAssets.Length == 1 && Path.GetFullPath(movedAssets[0]) == dpoPath)) + return; + + /* If the only change is an import then check if the imported file + * is the same as the last, and if so check into returning early. + * For some reason occasionally when files are saved unity runs postprocess + * multiple times on the same file. */ + string imported = (importedAssets.Length == 1) ? importedAssets[0] : null; + if (imported != null && imported == _lastSingleImportedAsset) + { + //If here then the file is the same. Make sure it's already in the collection before returning. + System.Type assetType = AssetDatabase.GetMainAssetTypeAtPath(imported); + //Not a gameObject, no reason to continue. + if (assetType != typeof(GameObject)) + return; + + NetworkObject nob = AssetDatabase.LoadAssetAtPath(imported); + //If is a networked object. + if (nob != null) + { + //Already added! + if (prefabCollection.Prefabs.Contains(nob)) + return; + } + } + else if (imported != null) + { + _lastSingleImportedAsset = imported; + } + } + + + bool fullRebuild = settings.FullRebuild; + /* If updating FN. This needs to be done a better way. + * Parsing the actual version file would be better. + * I'll get to it next release. */ + if (!_ranOnce) + { + _ranOnce = true; + fullRebuild = true; + } + else + { + CheckForVersionFile(importedAssets); + CheckForVersionFile(deletedAssets); + CheckForVersionFile(movedAssets); + CheckForVersionFile(movedFromAssetPaths); + } + + /* See if any of the changed files are the version file. + * A new version file suggests an update. Granted, this could occur if + * other assets imported a new version file as well but better + * safe than sorry. */ + void CheckForVersionFile(string[] arr) + { + string targetText = "VERSION.txt".ToLower(); + int targetLength = targetText.Length; + + for (int i = 0; i < arr.Length; i++) + { + string item = arr[i]; + int itemLength = item.Length; + if (itemLength < targetLength) + continue; + + item = item.ToLower(); + int startIndex = (itemLength - targetLength); + if (item.Substring(startIndex, targetLength) == targetText) + { + fullRebuild = true; + return; + } + } + } + + if (fullRebuild) + GenerateFull(settings); + else + GenerateChanged(importedAssets, deletedAssets, movedAssets, movedFromAssetPaths, settings); + } + } + } +} + +#endif diff --git a/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/Generator.cs.meta b/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/Generator.cs.meta new file mode 100644 index 0000000..20b7c5c --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/Generator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 68e990388e202d54aa0fe9e7aa8cc716 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/Settings.cs b/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/Settings.cs new file mode 100644 index 0000000..82a20af --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/Settings.cs @@ -0,0 +1,103 @@ +#if UNITY_EDITOR + +using System; +using System.Collections.Generic; +using System.IO; +using UnityEngine; + +namespace FishNet.Editing.PrefabCollectionGenerator +{ + internal sealed class PrefabCollectionGeneratorConfigurationsz + { + #region Types. + public enum SearchScopeType : byte + { + EntireProject = 0, + SpecificFolders = 1 + } + #endregion + + //#region Public. + ///// + ///// True if prefab generation is enabled. + ///// + //public bool Enabled; + ///// + ///// True to rebuild all prefabs during any change. False to only check changed prefabs. + ///// + //public bool FullRebuild; + ///// + ///// True to log results to console. + ///// + //public bool LogToConsole; + ///// + ///// True to automatically save assets when default prefabs change. + ///// + //public bool SaveChanges; + ///// + ///// Path where prefabs file is created. + ///// + //public string AssetPath; + ///// + ///// How to search for files. + ///// + //public SearchScopeType SearchScope = SearchScopeType.EntireProject; + ///// + ///// Folders to exclude when using SearchScopeType.SpecificFolders. + ///// + //public List ExcludedFolders = new List(); + ///// + ///// Folders to include when using SearchScopeType.SpecificFolders. + ///// + //public List IncludedFolders = new List(); + //#endregion + + //#region Private. + ///// + ///// Library folder for project. Presumably where files are saved, but this is changing. This is going away in favor of FN config. //fnconfig. + ///// + //private static string DirectoryPath => Path.Combine(Path.GetDirectoryName(Application.dataPath), "Library"); + ///// + ///// Full path of settings file. This is going away in favor of FN config. //fnconfig. + ///// + //private static string FilePath => Path.Combine(DirectoryPath, $"FishNet.Runtime.Editor.PrefabObjects.Generation.{nameof(Settings)}.json"); + // #endregion + + // public Settings() + //{ + // Enabled = true; + // LogToConsole = true; + // FullRebuild = false; + // SaveChanges = true; + // SearchScope = SearchScopeType.EntireProject; + + // AssetPath = $"Assets{Path.DirectorySeparatorChar}FishNet{Path.DirectorySeparatorChar}Runtime{Path.DirectorySeparatorChar}DefaultPrefabObjects.asset"; + //} + + //public void Save() + //{ + // //Create save folder if it doesn't exist. This is going away in favor of FN config. //fnconfig. + // if (!Directory.Exists(DirectoryPath)) + // Directory.CreateDirectory(DirectoryPath); + + // File.WriteAllText(FilePath, JsonUtility.ToJson(this)); + //} + + //public static Settings Load() + //{ + // try + // { + // if (File.Exists(FilePath)) + // return JsonUtility.FromJson(File.ReadAllText(FilePath)); + // } + // catch (Exception ex) + // { + // Debug.LogError($"An error has occurred when loading the prefab collection generator settings: {ex}"); + // } + + // return new Settings(); + //} + } +} + +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/Settings.cs.meta b/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/Settings.cs.meta new file mode 100644 index 0000000..ef397a2 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/Settings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0fa5ae5e1c43e004c87577ab53180f70 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/SettingsProvider.cs b/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/SettingsProvider.cs new file mode 100644 index 0000000..c3a73bf --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/SettingsProvider.cs @@ -0,0 +1,238 @@ +#if UNITY_EDITOR + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; +using UnityEditor; +using UnityEngine; + +using UnitySettingsProviderAttribute = UnityEditor.SettingsProviderAttribute; +using UnitySettingsProvider = UnityEditor.SettingsProvider; +using FishNet.Configuring; +using System.Linq; + +namespace FishNet.Editing.PrefabCollectionGenerator +{ + internal static class SettingsProvider + { + private static readonly Regex SlashRegex = new Regex(@"[\\//]"); + + private static PrefabGeneratorConfigurations _settings; + + private static GUIContent _folderIcon; + private static GUIContent _deleteIcon; + + private static Vector2 _scrollVector; + + private static bool _showFolders; + + [UnitySettingsProvider] + private static UnitySettingsProvider Create() + { + return new UnitySettingsProvider("Project/Fish-Networking/Prefab Objects Generator", SettingsScope.Project) + { + label = "Prefab Objects Generator", + + guiHandler = OnGUI, + + keywords = new string[] + { + "Fish", + "Networking", + "Prefab", + "Objects", + "Generator", + }, + }; + } + + private static void OnGUI(string searchContext) + { + if (_settings == null) + _settings = Configuration.Configurations.PrefabGenerator; + if (_folderIcon == null) + _folderIcon = EditorGUIUtility.IconContent("d_FolderOpened Icon"); + if (_deleteIcon == null) + _deleteIcon = EditorGUIUtility.IconContent("P4_DeletedLocal"); + + EditorGUI.BeginChangeCheck(); + GUIStyle scrollViewStyle = new GUIStyle() + { + padding = new RectOffset(10, 10, 10, 10), + }; + + _scrollVector = EditorGUILayout.BeginScrollView(_scrollVector, scrollViewStyle); + + _settings.Enabled = EditorGUILayout.Toggle(ObjectNames.NicifyVariableName(nameof(_settings.Enabled)), _settings.Enabled); + _settings.LogToConsole = EditorGUILayout.Toggle(ObjectNames.NicifyVariableName(nameof(_settings.LogToConsole)), _settings.LogToConsole); + _settings.FullRebuild = EditorGUILayout.Toggle(ObjectNames.NicifyVariableName(nameof(_settings.FullRebuild)), _settings.FullRebuild); + _settings.SaveChanges = EditorGUILayout.Toggle(ObjectNames.NicifyVariableName(nameof(_settings.SaveChanges)), _settings.SaveChanges); + + GUILayoutOption iconWidthConstraint = GUILayout.MaxWidth(32.0f); + GUILayoutOption iconHeightConstraint = GUILayout.MaxHeight(EditorGUIUtility.singleLineHeight); + + EditorGUILayout.BeginHorizontal(); + + string oldAssetPath = _settings.DefaultPrefabObjectsPath; + string newAssetPath = EditorGUILayout.DelayedTextField(ObjectNames.NicifyVariableName(nameof(_settings.DefaultPrefabObjectsPath)), oldAssetPath); + + if (GUILayout.Button(_folderIcon, iconWidthConstraint, iconHeightConstraint)) + { + if (TrySaveFilePathInsideAssetsFolder(null, Application.dataPath, "DefaultPrefabObjects", "asset", out string result)) + newAssetPath = result; + else + EditorWindow.focusedWindow.ShowNotification(new GUIContent($"{ObjectNames.NicifyVariableName(nameof(_settings.DefaultPrefabObjectsPath))} must be inside the Assets folder.")); + } + + if (!newAssetPath.Equals(oldAssetPath, StringComparison.OrdinalIgnoreCase)) + { + if (newAssetPath.StartsWith($"Assets{Path.DirectorySeparatorChar}", StringComparison.OrdinalIgnoreCase)) + { + if (File.Exists(newAssetPath)) + { + EditorWindow.focusedWindow.ShowNotification(new GUIContent("Another asset already exists at the new path.")); + } + else + { + Generator.IgnorePostProcess = true; + + if (File.Exists(oldAssetPath)) + AssetDatabase.MoveAsset(oldAssetPath, newAssetPath); + _settings.DefaultPrefabObjectsPath = newAssetPath; + + Generator.IgnorePostProcess = false; + } + } + else + { + EditorWindow.focusedWindow.ShowNotification(new GUIContent($"{ObjectNames.NicifyVariableName(nameof(_settings.DefaultPrefabObjectsPath))} must be inside the Assets folder.")); + } + } + + EditorGUILayout.EndHorizontal(); + + int currentSearchScope = _settings.SearchScope; + SearchScopeType searchScopeType = (SearchScopeType)EditorGUILayout.EnumPopup(ValueToSearchScope(_settings.SearchScope)); + _settings.SearchScope = (int)searchScopeType; + SearchScopeType ValueToSearchScope(int value) => (SearchScopeType)value; + if (_settings.SearchScope == (int)SearchScopeType.EntireProject) + { + EditorGUILayout.HelpBox("Searching the entire project for prefabs can become very slow. Consider switching the search scope to specific folders instead.", MessageType.Warning); + + if (GUILayout.Button("Switch")) + _settings.SearchScope = (int)SearchScopeType.SpecificFolders; + } + //If search scope changed then update prefabs. + if (currentSearchScope != _settings.SearchScope && (SearchScopeType)_settings.SearchScope == SearchScopeType.EntireProject) + Generator.GenerateFull(); + + List folders = null; + string foldersName = null; + + if (_settings.SearchScope == (int)SearchScopeType.EntireProject) + { + folders = _settings.ExcludedFolders; + foldersName = ObjectNames.NicifyVariableName(nameof(_settings.ExcludedFolders)); + } + else if (_settings.SearchScope == (int)SearchScopeType.SpecificFolders) + { + folders = _settings.IncludedFolders; + foldersName = ObjectNames.NicifyVariableName(nameof(_settings.IncludedFolders)); + } + + string folderName = foldersName.Substring(0, foldersName.Length - 1); + + if ((_showFolders = EditorGUILayout.Foldout(_showFolders, $"{foldersName} ({folders.Count})")) && folders != null) + { + EditorGUI.indentLevel++; + + for (int i = 0; i < folders.Count; i++) + { + EditorGUILayout.BeginHorizontal(); + + string oldFolder = folders[i]; + string newFolder = SlashRegex.Replace(EditorGUILayout.DelayedTextField(oldFolder), Path.DirectorySeparatorChar.ToString()); + if (!newFolder.Equals(oldFolder, StringComparison.OrdinalIgnoreCase)) + { + if (newFolder.StartsWith($"Assets{Path.DirectorySeparatorChar}", StringComparison.OrdinalIgnoreCase)) + folders[i] = newFolder; + else + EditorWindow.focusedWindow.ShowNotification(new GUIContent($"{folderName} must be inside the Assets folder.")); + } + + if (GUILayout.Button(_folderIcon, iconWidthConstraint, iconHeightConstraint)) + { + if (TryOpenFolderPathInsideAssetsFolder(null, Application.dataPath, null, out string result)) + folders[i] = result; + else + EditorWindow.focusedWindow.ShowNotification(new GUIContent($"{folderName} must be inside the Assets folder.")); + } + + if (GUILayout.Button(_deleteIcon, iconWidthConstraint, iconHeightConstraint)) folders.RemoveAt(i); + + EditorGUILayout.EndHorizontal(); + } + + EditorGUI.indentLevel--; + + if (_settings.SearchScope == (int)SearchScopeType.SpecificFolders) EditorGUILayout.HelpBox("You can include subfolders by appending an asterisk (*) to a path.", MessageType.None); + + if (GUILayout.Button("Browse")) + { + if (TryOpenFolderPathInsideAssetsFolder(null, Application.dataPath, null, out string result)) + { + folders.Add(result); + } + else + { + EditorWindow.focusedWindow.ShowNotification(new GUIContent($"{folderName} must be inside the Assets folder.")); + } + } + } + + if (EditorGUI.EndChangeCheck()) + Configuration.Configurations.Write(true); + if (GUILayout.Button("Generate")) + Generator.GenerateFull(); + + EditorGUILayout.HelpBox("Consider pressing 'Generate' after changing the settings.", MessageType.Info); + + EditorGUILayout.EndScrollView(); + } + + private static bool TrySaveFilePathInsideAssetsFolder(string title, string directory, string name, string extension, out string result) + { + result = null; + + string selectedPath = EditorUtility.SaveFilePanel(title, directory, name, extension); + + if (selectedPath.StartsWith(Application.dataPath, StringComparison.OrdinalIgnoreCase)) + { + result = SlashRegex.Replace(selectedPath.Remove(0, Path.GetDirectoryName(Application.dataPath).Length + 1), Path.DirectorySeparatorChar.ToString()); + + return true; + } + + return false; + } + + private static bool TryOpenFolderPathInsideAssetsFolder(string title, string folder, string name, out string result) + { + result = null; + + string selectedPath = EditorUtility.OpenFolderPanel(title, folder, name); + + if (selectedPath.StartsWith(Application.dataPath, StringComparison.OrdinalIgnoreCase)) + { + result = SlashRegex.Replace(selectedPath.Remove(0, Path.GetDirectoryName(Application.dataPath).Length + 1), Path.DirectorySeparatorChar.ToString()); + + return true; + } + + return false; + } + } +} + +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/SettingsProvider.cs.meta b/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/SettingsProvider.cs.meta new file mode 100644 index 0000000..01a488e --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/SettingsProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7451fcc5eeb5b89468ab2ce22f678b26 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/PrefabProcessor.cs b/Assets/FishNet/Runtime/Editor/PrefabProcessor.cs new file mode 100644 index 0000000..ea1fa69 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/PrefabProcessor.cs @@ -0,0 +1,85 @@ +//#if UNITY_EDITOR +//using FishNet.Managing.Object; +//using FishNet.Object; +//using UnityEditor; +//using UnityEngine; + +//namespace FishNet.Editing +//{ +// internal class PrefabProcessor : AssetPostprocessor +// { +// #region Private. +// /// +// /// ScriptableObject to store default prefabs. +// /// +// private static DefaultPrefabObjects _defaultPrefabs; +// #endregion + +// /// +// /// Called after assets are created or imported. +// /// +// /// +// /// +// /// +// /// +//#if UNITY_2021_3_OR_NEWER +// private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths, bool didDomainReload) +//#else +// private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) +//#endif +// { + +//#if UNITY_2021_3_OR_NEWER +// if (didDomainReload) +// return; +//#endif +// bool justPopulated; +// if (_defaultPrefabs == null) +// _defaultPrefabs = DefaultPrefabsFinder.GetDefaultPrefabsFile(out justPopulated); +// else +// justPopulated = DefaultPrefabsFinder.PopulateDefaultPrefabs(); +// //Not found. +// if (_defaultPrefabs == null) +// return; + +// //True if null must be removed as well. +// bool removeNull = (deletedAssets.Length > 0 || movedAssets.Length > 0 || movedFromAssetPaths.Length > 0); +// if (removeNull) +// _defaultPrefabs.RemoveNull(); + +// /* Only need to add new prefabs if not justPopulated. +// * justPopulated would have already picked up the new prefabs. */ +// if (justPopulated) +// return; + +// System.Type goType = typeof(UnityEngine.GameObject); +// foreach (string item in importedAssets) +// { +// System.Type assetType = AssetDatabase.GetMainAssetTypeAtPath(item); +// if (assetType != goType) +// continue; + +// GameObject go = (GameObject)AssetDatabase.LoadAssetAtPath(item, typeof(GameObject)); +// //If is a gameobject. +// if (go != null) +// { + +// NetworkObject nob; +// //Not a network object. +// if (!go.TryGetComponent(out nob)) +// continue; + +// /* Check for duplicates because adding a component to a prefab will also call this function +// * which will result in this function calling multiple times for the same object. */ +// _defaultPrefabs.AddObject(nob, true); +// } +// } + +// EditorUtility.SetDirty(_defaultPrefabs); +// } + +// } + +//} + +//#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Editor/PrefabProcessor.cs.meta b/Assets/FishNet/Runtime/Editor/PrefabProcessor.cs.meta new file mode 100644 index 0000000..5c1d41e --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/PrefabProcessor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f78656ace4bbad94288ff6238a2b518c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/ScriptingDefines.cs b/Assets/FishNet/Runtime/Editor/ScriptingDefines.cs new file mode 100644 index 0000000..2ef6eae --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/ScriptingDefines.cs @@ -0,0 +1,74 @@ +#if UNITY_EDITOR +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace FishNet +{ + internal static class ScriptingDefines + { + [InitializeOnLoadMethod] + public static void AddDefineSymbols() + { + string currentDefines = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup); + /* Convert current defines into a hashset. This is so we can + * determine if any of our defines were added. Only save playersettings + * when a define is added. */ + HashSet definesHs = new HashSet(); + string[] currentArr = currentDefines.Split(';'); + //Add current defines into hs. + foreach (string item in currentArr) + definesHs.Add(item); + + string proDefine = "FISHNET_PRO"; + string versionPrefix = "FISHNET_V"; + string thisVersion = $"{versionPrefix}3"; + string[] fishNetDefines = new string[] + { + "FISHNET", + thisVersion, + + }; + bool modified = false; + //Now add FN defines. + foreach (string item in fishNetDefines) + modified |= definesHs.Add(item); + + /* Remove pro define if not on pro. This might look a little + * funny because the code below varies depending on if pro or not. */ + +#pragma warning disable CS0162 // Unreachable code detected + modified |= definesHs.Remove(proDefine); +#pragma warning restore CS0162 // Unreachable code detected + + List definesToRemove = new List(); + int versionPrefixLength = versionPrefix.Length; + //Remove old versions. + foreach (string item in definesHs) + { + //Do not remove this version. + if (item == thisVersion) + continue; + + //If length is possible to be a version prefix and is so then remove it. + if (item.Length >= versionPrefixLength && item.Substring(0, versionPrefixLength) == versionPrefix) + definesToRemove.Add(item); + } + + modified |= (definesToRemove.Count > 0); + foreach (string item in definesToRemove) + { + definesHs.Remove(item); + Debug.Log($"Removed unused Fish-Networking define {item}."); + } + + if (modified) + { + Debug.Log("Added or removed Fish-Networking defines within player settings."); + string changedDefines = string.Join(";", definesHs); + PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, changedDefines); + } + } + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Editor/ScriptingDefines.cs.meta b/Assets/FishNet/Runtime/Editor/ScriptingDefines.cs.meta new file mode 100644 index 0000000..4ebc6d2 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/ScriptingDefines.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 893e5074d486a0e4aaf7803436fef791 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/Textures.meta b/Assets/FishNet/Runtime/Editor/Textures.meta new file mode 100644 index 0000000..c8e8ef0 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Textures.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0c9efa228205fac47af86971242e97f9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/Textures/Icon.meta b/Assets/FishNet/Runtime/Editor/Textures/Icon.meta new file mode 100644 index 0000000..ed9af2f --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Textures/Icon.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cc19ac8f58640dd4bb062b1024d23ecc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/Textures/Icon/fishnet_light.png b/Assets/FishNet/Runtime/Editor/Textures/Icon/fishnet_light.png new file mode 100644 index 0000000..084c0e6 Binary files /dev/null and b/Assets/FishNet/Runtime/Editor/Textures/Icon/fishnet_light.png differ diff --git a/Assets/FishNet/Runtime/Editor/Textures/Icon/fishnet_light.png.meta b/Assets/FishNet/Runtime/Editor/Textures/Icon/fishnet_light.png.meta new file mode 100644 index 0000000..39422a9 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Textures/Icon/fishnet_light.png.meta @@ -0,0 +1,92 @@ +fileFormatVersion: 2 +guid: bf9191e2e07d29749bca3a1ae44e4bc8 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/Textures/UI.meta b/Assets/FishNet/Runtime/Editor/Textures/UI.meta new file mode 100644 index 0000000..9474195 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Textures/UI.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dff09a4ee67e87a4083af1583c025766 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/Textures/UI/Button_Dark_Client_Background.png b/Assets/FishNet/Runtime/Editor/Textures/UI/Button_Dark_Client_Background.png new file mode 100644 index 0000000..89c94ab Binary files /dev/null and b/Assets/FishNet/Runtime/Editor/Textures/UI/Button_Dark_Client_Background.png differ diff --git a/Assets/FishNet/Runtime/Editor/Textures/UI/Button_Dark_Client_Background.png.meta b/Assets/FishNet/Runtime/Editor/Textures/UI/Button_Dark_Client_Background.png.meta new file mode 100644 index 0000000..be69e05 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Textures/UI/Button_Dark_Client_Background.png.meta @@ -0,0 +1,116 @@ +fileFormatVersion: 2 +guid: 2d50394614f8feb4eb0567fb7618d84d +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/Textures/UI/Button_Dark_Indicator.png b/Assets/FishNet/Runtime/Editor/Textures/UI/Button_Dark_Indicator.png new file mode 100644 index 0000000..80192f5 Binary files /dev/null and b/Assets/FishNet/Runtime/Editor/Textures/UI/Button_Dark_Indicator.png differ diff --git a/Assets/FishNet/Runtime/Editor/Textures/UI/Button_Dark_Indicator.png.meta b/Assets/FishNet/Runtime/Editor/Textures/UI/Button_Dark_Indicator.png.meta new file mode 100644 index 0000000..7eb6afd --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Textures/UI/Button_Dark_Indicator.png.meta @@ -0,0 +1,116 @@ +fileFormatVersion: 2 +guid: 2b3dca501a9d8c8479dc71dd068aa8b8 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/Textures/UI/Button_Dark_Server_Background.png b/Assets/FishNet/Runtime/Editor/Textures/UI/Button_Dark_Server_Background.png new file mode 100644 index 0000000..f4d8573 Binary files /dev/null and b/Assets/FishNet/Runtime/Editor/Textures/UI/Button_Dark_Server_Background.png differ diff --git a/Assets/FishNet/Runtime/Editor/Textures/UI/Button_Dark_Server_Background.png.meta b/Assets/FishNet/Runtime/Editor/Textures/UI/Button_Dark_Server_Background.png.meta new file mode 100644 index 0000000..54f3c33 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Textures/UI/Button_Dark_Server_Background.png.meta @@ -0,0 +1,116 @@ +fileFormatVersion: 2 +guid: 1b187e63031bf7849b249c8212440c3b +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/Textures/UI/Logo_With_Text.png b/Assets/FishNet/Runtime/Editor/Textures/UI/Logo_With_Text.png new file mode 100644 index 0000000..5e5d02e Binary files /dev/null and b/Assets/FishNet/Runtime/Editor/Textures/UI/Logo_With_Text.png differ diff --git a/Assets/FishNet/Runtime/Editor/Textures/UI/Logo_With_Text.png.meta b/Assets/FishNet/Runtime/Editor/Textures/UI/Logo_With_Text.png.meta new file mode 100644 index 0000000..23c249b --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Textures/UI/Logo_With_Text.png.meta @@ -0,0 +1,96 @@ +fileFormatVersion: 2 +guid: 5ea1cf1e0e57aff4e9ad3cd4246b0e80 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/FishNet.Runtime.asmdef b/Assets/FishNet/Runtime/FishNet.Runtime.asmdef new file mode 100644 index 0000000..ea60e5c --- /dev/null +++ b/Assets/FishNet/Runtime/FishNet.Runtime.asmdef @@ -0,0 +1,17 @@ +{ + "name": "FishNet.Runtime", + "references": [ + "GUID:9e24947de15b9834991c9d8411ea37cf", + "GUID:84651a3751eca9349aac36a66bba901b", + "GUID:69448af7b92c7f342b298e06a37122aa" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/FishNet.Runtime.asmdef.meta b/Assets/FishNet/Runtime/FishNet.Runtime.asmdef.meta new file mode 100644 index 0000000..f153703 --- /dev/null +++ b/Assets/FishNet/Runtime/FishNet.Runtime.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7c88a4a7926ee5145ad2dfa06f454c67 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated.meta b/Assets/FishNet/Runtime/Generated.meta new file mode 100644 index 0000000..396b552 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ff7d76a12b1ab04449cefe1ba58b5cde +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component.meta b/Assets/FishNet/Runtime/Generated/Component.meta new file mode 100644 index 0000000..9074fd6 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 953b8f1e48e9769439f19dc81a0406dc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator.meta b/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator.meta new file mode 100644 index 0000000..b910d85 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 40310629b512873468cfaf757b6fd377 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/Editor.meta b/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/Editor.meta new file mode 100644 index 0000000..a5402a7 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e5ab06c5b11d85d4688a573ad0fdefdd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/Editor/NetworkAnimatorEditor.cs b/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/Editor/NetworkAnimatorEditor.cs new file mode 100644 index 0000000..6e7576b --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/Editor/NetworkAnimatorEditor.cs @@ -0,0 +1,187 @@ +#if UNITY_EDITOR +using FishNet.Editing; +using System.Collections.Generic; +using UnityEditor; +using UnityEditor.Animations; +using UnityEngine; + +namespace FishNet.Component.Animating.Editing +{ + + [CustomEditor(typeof(NetworkAnimator), true)] + [CanEditMultipleObjects] + public class NetworkAnimatorEditor : Editor + { + private SerializedProperty _animator; + private SerializedProperty _interpolation; + //private SerializedProperty _synchronizeInterval; + private SerializedProperty _smoothFloats; + private SerializedProperty _clientAuthoritative; + private SerializedProperty _sendToOwner; + private RuntimeAnimatorController _lastRuntimeAnimatorController; + private AnimatorController _lastAnimatorController; + + protected virtual void OnEnable() + { + _animator = serializedObject.FindProperty("_animator"); + _interpolation = serializedObject.FindProperty("_interpolation"); + //_synchronizeInterval = serializedObject.FindProperty("_synchronizeInterval"); + _smoothFloats = serializedObject.FindProperty("_smoothFloats"); + + _clientAuthoritative = serializedObject.FindProperty("_clientAuthoritative"); + _sendToOwner = serializedObject.FindProperty("_sendToOwner"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + NetworkAnimator na = (NetworkAnimator)target; + + GUI.enabled = false; + EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour(na), typeof(NetworkAnimator), false); + GUI.enabled = true; + + +#pragma warning disable CS0162 // Unreachable code detected + EditorGUILayout.HelpBox(EditingConstants.PRO_ASSETS_LOCKED_TEXT, MessageType.Warning); +#pragma warning restore CS0162 // Unreachable code detected + + //Animator + EditorGUILayout.LabelField("Animator", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_animator, new GUIContent("Animator", "The animator component to synchronize.")); + EditorGUI.indentLevel--; + EditorGUILayout.Space(); + + //Synchronization Processing. + EditorGUILayout.LabelField("Synchronization Processing", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_interpolation); + //EditorGUILayout.PropertyField(_synchronizeInterval, new GUIContent("Synchronize Interval", "How often to synchronize this animator.")); + EditorGUILayout.PropertyField(_smoothFloats, new GUIContent("Smooth Floats", "True to smooth floats on spectators rather than snap to their values immediately. Commonly set to true for smooth blend tree animations.")); + EditorGUI.indentLevel--; + EditorGUILayout.Space(); + + //Authority. + EditorGUILayout.LabelField("Authority", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_clientAuthoritative, new GUIContent("Client Authoritative", "True if using client authoritative movement.")); + if (_clientAuthoritative.boolValue == false) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_sendToOwner, new GUIContent("Synchronize To Owner", "True to synchronize server results back to owner. Typically used when you are sending inputs to the server and are relying on the server response to move the transform.")); + EditorGUI.indentLevel--; + } + EditorGUI.indentLevel--; + EditorGUILayout.Space(); + + DrawParameters(na); + + serializedObject.ApplyModifiedProperties(); + } + + + private void DrawParameters(NetworkAnimator na) + { + EditorGUILayout.LabelField("* Synchronized Parameters", EditorStyles.boldLabel); + EditorGUILayout.HelpBox("This setting allows you to optionally completely prevent the synchronization of certain parameters. Both Fish-Networking free and Pro will only synchronize changes as they occur.", MessageType.Info); + + if (Application.isPlaying) + { + EditorGUILayout.HelpBox("This feature can only be configured while out of play mode.", MessageType.Info); + return; + } + if (na == null) + return; + Animator animator = na.Animator; + if (animator == null) + return; + + RuntimeAnimatorController runtimeController = (animator.runtimeAnimatorController is AnimatorOverrideController aoc) ? aoc.runtimeAnimatorController : animator.runtimeAnimatorController; + if (runtimeController == null) + { + na.IgnoredParameters.Clear(); + return; + } + + /* If runtime controller changed + * or editor controller is null + * then get new editor controller. */ + if (runtimeController != _lastRuntimeAnimatorController || _lastAnimatorController == null) + _lastAnimatorController = (AnimatorController)AssetDatabase.LoadAssetAtPath(AssetDatabase.GetAssetPath(runtimeController), typeof(AnimatorController)); + _lastRuntimeAnimatorController = runtimeController; + + Color defaultColor = GUI.backgroundColor; + float width = Screen.width; + float spacePerEntry = 125f; + //Buttons seem to be longer than spacePerEntry. Why, because who knows... + float extraSpaceJustBecause = 60; + float spacer = 20f; + width -= spacer; + int entriesPerWidth = Mathf.Max(1, Mathf.FloorToInt(width / (spacePerEntry + extraSpaceJustBecause))); + + List aps = new List(); + //Create a parameter detail for each parameter that can be synchronized. + int count = 0; + foreach (AnimatorControllerParameter item in _lastAnimatorController.parameters) + { + count++; + //Over 240 parameters; who would do this!? + if (count >= 240) + continue; + + aps.Add(item); + } + + int apsCount = aps.Count; + for (int i = 0; i < apsCount; i++) + { + using (GUILayout.HorizontalScope hs = new GUILayout.HorizontalScope()) + { + GUILayout.Space(spacer); + int z = 0; + while (z < entriesPerWidth && (z + i < apsCount)) + { + //If this z+i would exceed entries then break. + if (z + i >= apsCount) + break; + + AnimatorControllerParameter item = aps[i + z]; + string parameterName = item.name; + bool ignored = na.IgnoredParameters.Contains(parameterName); + + Color c = (ignored) ? Color.gray : Color.green; + GUI.backgroundColor = c; + if (GUILayout.Button(item.name, GUILayout.Width(spacePerEntry))) + { + if (Application.isPlaying) + { + Debug.Log("Synchronized parameters may not be changed while playing."); + } + else + { + if (ignored) + na.IgnoredParameters.Remove(parameterName); + else + na.IgnoredParameters.Add(parameterName); + } + } + + z++; + } + + i += (z - 1); + } + + GUI.backgroundColor = defaultColor; + } + } + + + + } + +} + + +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/Editor/NetworkAnimatorEditor.cs.meta b/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/Editor/NetworkAnimatorEditor.cs.meta new file mode 100644 index 0000000..c72bdf3 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/Editor/NetworkAnimatorEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 65609e99a0823a347a2f615b0e6f736e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/NetworkAnimator.cs b/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/NetworkAnimator.cs new file mode 100644 index 0000000..e33cdfc --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/NetworkAnimator.cs @@ -0,0 +1,1449 @@ +using FishNet.Component.Transforming; +using FishNet.Connection; +using FishNet.Documenting; +using FishNet.Managing.Logging; +using FishNet.Managing.Server; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Utility; +using FishNet.Utility.Performance; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + + +namespace FishNet.Component.Animating +{ + [AddComponentMenu("FishNet/Component/NetworkAnimator")] + public sealed class NetworkAnimator : NetworkBehaviour + { + #region Types. + /// + /// Data received from the server. + /// + private struct ReceivedServerData + { + /// + /// Gets an Arraysegment of received data. + /// + public ArraySegment GetArraySegment() => new ArraySegment(_data, 0, _length); + /// + /// How much data written. + /// + private int _length; + /// + /// Buffer which contains data. + /// + private byte[] _data; + + public ReceivedServerData(ArraySegment segment) + { + _length = segment.Count; + _data = ByteArrayPool.Retrieve(_length); + Buffer.BlockCopy(segment.Array, segment.Offset, _data, 0, _length); + } + + public void Dispose() + { + if (_data != null) + ByteArrayPool.Store(_data); + } + } + private struct StateChange + { + /// + /// True if a crossfade. + /// + public bool IsCrossfade; + /// + /// Hash to crossfade into. + /// + public int Hash; + /// + /// True if using FixedTime. + /// + public bool FixedTime; + /// + /// Duration of crossfade. + /// + public float DurationTime; + /// + /// Offset time of crossfade. + /// + public float OffsetTime; + /// + /// Normalized transition time of crossfade. + /// + public float NormalizedTransitionTime; + + public StateChange(int hash, bool fixedTime, float duration, float offset, float normalizedTransition) + { + IsCrossfade = true; + Hash = hash; + FixedTime = fixedTime; + DurationTime = duration; + OffsetTime = offset; + NormalizedTransitionTime = normalizedTransition; + } + } + /// + /// Animator updates received from clients when using Client Authoritative. + /// + private class ClientAuthoritativeUpdate + { + /// + /// + /// + public ClientAuthoritativeUpdate() + { + //Start buffers off at 8 bytes nad grow them as needed. + for (int i = 0; i < MAXIMUM_BUFFER_COUNT; i++) + _buffers.Add(new byte[MAXIMUM_DATA_SIZE]); + + _bufferLengths = new int[MAXIMUM_BUFFER_COUNT]; + } + + #region Public. + /// + /// True to force all animator data and ignore buffers. + /// + public bool ForceAll { get; private set; } + /// + /// Number of entries in Buffers. + /// + public int BufferCount = 0; + #endregion + + #region Private. + /// + /// Length of buffers. + /// + private int[] _bufferLengths; + /// + /// Buffers. + /// + private List _buffers = new List(); + #endregion + + #region Const. + /// + /// Maximum size data may be. + /// + private const int MAXIMUM_DATA_SIZE = 1000; + /// + /// Maximum number of allowed buffers. + /// + public const int MAXIMUM_BUFFER_COUNT = 2; + #endregion + + public void AddToBuffer(ref ArraySegment data) + { + int dataCount = data.Count; + /* Data will never get this large, it's quite impossible. + * Just ignore the data if it does, client is likely performing + * an attack. */ + if (dataCount > MAXIMUM_DATA_SIZE) + return; + + //If index exceeds buffer count. + if (BufferCount >= MAXIMUM_BUFFER_COUNT) + { + ForceAll = true; + return; + } + + /* If here, can write to buffer. */ + byte[] buffer = _buffers[BufferCount]; + Buffer.BlockCopy(data.Array, data.Offset, buffer, 0, dataCount); + _bufferLengths[BufferCount] = dataCount; + BufferCount++; + } + + /// + /// Sets referenced data to buffer and it's length for index. + /// + /// + /// + /// + public void GetBuffer(int index, ref byte[] buffer, ref int length) + { + if (index > _buffers.Count) + { + Debug.LogWarning("Index exceeds Buffers count."); + return; + } + if (index > _bufferLengths.Length) + { + Debug.LogWarning("Index exceeds BufferLengths count."); + return; + } + + buffer = _buffers[index]; + length = _bufferLengths[index]; + } + /// + /// Resets buffers. + /// + public void Reset() + { + BufferCount = 0; + ForceAll = false; + } + + } + /// + /// Information on how to smooth to a float value. + /// + private struct SmoothedFloat + { + public SmoothedFloat(float rate, float target) + { + Rate = rate; + Target = target; + } + + public readonly float Rate; + public readonly float Target; + } + + /// + /// Details about a trigger update. + /// + private struct TriggerUpdate + { + public byte ParameterIndex; + public bool Setting; + + public TriggerUpdate(byte parameterIndex, bool setting) + { + ParameterIndex = parameterIndex; + Setting = setting; + } + } + /// + /// Details about an animator parameter. + /// + private class ParameterDetail + { + /// + /// Parameter information. + /// + public readonly AnimatorControllerParameter ControllerParameter = null; + /// + /// Index within the types collection for this parameters value. The exception is with triggers; if the parameter type is a trigger then a value of 1 is set, 0 is unset. + /// + public readonly byte TypeIndex = 0; + /// + /// Hash for the animator string. + /// + public readonly int Hash; + + public ParameterDetail(AnimatorControllerParameter controllerParameter, byte typeIndex) + { + ControllerParameter = controllerParameter; + TypeIndex = typeIndex; + Hash = controllerParameter.nameHash; + } + } + #endregion + + #region Public. + /// + /// Parameters which will not be synchronized. + /// + [SerializeField, HideInInspector] + internal List IgnoredParameters = new List(); + #endregion + + #region Serialized. + /// + /// The animator component to synchronize. + /// + [Tooltip("The animator component to synchronize.")] + [SerializeField] + private Animator _animator; + /// + /// The animator component to synchronize. + /// + public Animator Animator { get { return _animator; } } + /// + /// True to smooth float value changes for spectators. + /// + [Tooltip("True to smooth float value changes for spectators.")] + [SerializeField] + private bool _smoothFloats = true; + /// + /// How many ticks to interpolate. + /// + [Tooltip("How many ticks to interpolate.")] + [Range(1, NetworkTransform.MAX_INTERPOLATION)] + [SerializeField] + private ushort _interpolation = 2; + ///// + ///// How often to synchronize this animator. + ///// + //[Tooltip("How often to synchronize this animator.")] + //[Range(0.01f, 0.5f)] + //[SerializeField] + //private float _synchronizeInterval = 0.1f; + /// + /// + /// + [Tooltip("True if using client authoritative animations.")] + [SerializeField] + private bool _clientAuthoritative = true; + /// + /// True if using client authoritative animations. + /// + public bool ClientAuthoritative { get { return _clientAuthoritative; } } + /// + /// True to synchronize server results back to owner. Typically used when you are changing animations on the server and are relying on the server response to update the clients animations. + /// + [Tooltip("True to synchronize server results back to owner. Typically used when you are changing animations on the server and are relying on the server response to update the clients animations.")] + [SerializeField] + private bool _sendToOwner; + #endregion + + #region Private. + /// + /// All parameter values, excluding triggers. + /// + private List _parameterDetails = new List(); + /// + /// Last int values. + /// + private List _ints = new List(); + /// + /// Last float values. + /// + private List _floats = new List(); + /// + /// Last bool values. + /// + private List _bools = new List(); + /// + /// Last layer weights. + /// + private float[] _layerWeights; + /// + /// Last speed. + /// + private float _speed; + ///// + ///// Next time client may send parameter updates. + ///// + //private float _nextClientSendTime = -1f; + ///// + ///// Next time server may send parameter updates. + ///// + //private float _nextServerSendTime = -1f; + /// + /// Trigger values set by using SetTrigger and ResetTrigger. + /// + private List _triggerUpdates = new List(); + /// + /// Updates going to clients. + /// + private List _toClientsBuffer = new List(); + /// + /// Returns if the animator is exist and is active. + /// + private bool _isAnimatorEnabled + { + get + { + bool failedChecks = (_animator == null || !_animator.enabled || _animator.runtimeAnimatorController == null); + return !failedChecks; + } + } + /// + /// Float valeus to smooth towards. + /// + private Dictionary _smoothedFloats = new Dictionary(); + /// + /// Returns if floats can be smoothed for this client. + /// + private bool _canSmoothFloats + { + get + { + //Don't smooth on server only. + if (!base.IsClient) + return false; + //Smoothing is disabled. + if (!_smoothFloats) + return false; + //No reason to smooth for self. + if (base.IsOwner && ClientAuthoritative) + return false; + + //Fall through. + return true; + } + } + /// + /// Layers which need to have their state synchronized. Key is the layer, Value is the state change information. + /// + private Dictionary _unsynchronizedLayerStates = new Dictionary(); + /// + /// Layers which need to have their state blend synchronized. Key is ParameterIndex, Value is next state hash. + /// + //private Dictionary _unsynchronizedLayerStates = new HashSet(); + /// + /// Last animator set. + /// + private Animator _lastAnimator; + /// + /// Last Controller set. + /// + private RuntimeAnimatorController _lastController; + /// + /// PooledWriter for this animator. + /// + private PooledWriter _writer = new PooledWriter(); + /// + /// Holds client authoritative updates received to send to other clients. + /// + private ClientAuthoritativeUpdate _clientAuthoritativeUpdates; + /// + /// True to forceAll next timed send. + /// + private bool _forceAllOnTimed; + /// + /// Animations received which should be applied. + /// + private Queue _fromServerBuffer = new Queue(); + /// + /// Tick when the buffer may begin to run. + /// + private uint _startTick = uint.MaxValue; + #endregion + + #region Const. + ///// + ///// How much time to fall behind when using smoothing. Only increase value if the smoothing is sometimes jittery. Recommended values are between 0 and 0.04. + ///// + //private const float INTERPOLATION = 0.02f; + /// + /// ParameterDetails index which indicates a layer weight change. + /// + private const byte LAYER_WEIGHT = 240; + /// + /// ParameterDetails index which indicates an animator speed change. + /// + private const byte SPEED = 241; + /// + /// ParameterDetails index which indicates a layer state change. + /// + private const byte STATE = 242; + /// + /// ParameterDetails index which indicates a crossfade change. + /// + private const byte CROSSFADE = 243; + #endregion + + private void Awake() + { + InitializeOnce(); + } + + [APIExclude] + public override void OnSpawnServer(NetworkConnection connection) + { + base.OnSpawnServer(connection); + if (!_isAnimatorEnabled) + return; + if (AnimatorUpdated(out ArraySegment updatedBytes, true)) + TargetAnimatorUpdated(connection, updatedBytes); + } + + public override void OnStartNetwork() + { + base.OnStartNetwork(); + base.TimeManager.OnPreTick += TimeManager_OnPreTick; + base.TimeManager.OnPostTick += TimeManager_OnPostTick; + } + + [APIExclude] + public override void OnStartServer() + { + base.OnStartServer(); + //If using client authoritative then initialize clientAuthoritativeUpdates. + if (_clientAuthoritative) + { + _clientAuthoritativeUpdates = new ClientAuthoritativeUpdate(); + //Expand to clients buffer count to however many buffers can be held. + for (int i = 0; i < ClientAuthoritativeUpdate.MAXIMUM_BUFFER_COUNT; i++) + _toClientsBuffer.Add(new byte[0]); + } + else + { + _toClientsBuffer.Add(new byte[0]); + } + } + + public override void OnStopNetwork() + { + base.OnStopNetwork(); + base.TimeManager.OnPreTick -= TimeManager_OnPreTick; + base.TimeManager.OnPostTick -= TimeManager_OnPostTick; + } + + + /// + /// Called right before a tick occurs, as well before data is read. + /// + private void TimeManager_OnPreTick() + { + if (!_isAnimatorEnabled) + { + _fromServerBuffer.Clear(); + return; + } + //Disabled/cannot start. + if (_startTick == uint.MaxValue) + return; + //Nothing in queue. + if (_fromServerBuffer.Count == 0) + { + _startTick = uint.MaxValue; + return; + } + //Not enough time has passed to start queue. + if (base.TimeManager.LocalTick < _startTick) + return; + + ReceivedServerData rd = _fromServerBuffer.Dequeue(); + ArraySegment segment = rd.GetArraySegment(); + ApplyParametersUpdated(ref segment); + rd.Dispose(); + } + + + /* Use post tick values are checked after + * client has an opportunity to use OnTick. */ + /// + /// Called after a tick occurs; physics would have simulated if using PhysicsMode.TimeManager. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void TimeManager_OnPostTick() + { + //One check rather than per each method. + if (!_isAnimatorEnabled) + return; + + if (base.IsClient) + CheckSendToServer(); + if (base.IsServer) + CheckSendToClients(); + } + + private void Update() + { + if (!_isAnimatorEnabled) + return; + + if (base.IsClient) + SmoothFloats(); + } + + /// + /// Initializes this script for use. + /// + private void InitializeOnce() + { + if (_animator == null) + _animator = GetComponent(); + + //Don't run the rest if not in play mode. + if (!ApplicationState.IsPlaying()) + return; + + if (!_isAnimatorEnabled) + { + //Debug.LogWarning("Animator is null or not enabled; unable to initialize for animator. Use SetAnimator if animator was changed or enable the animator."); + return; + } + + //Speed. + _speed = _animator.speed; + + //Build layer weights. + _layerWeights = new float[_animator.layerCount]; + for (int i = 0; i < _layerWeights.Length; i++) + _layerWeights[i] = _animator.GetLayerWeight(i); + + _parameterDetails.Clear(); + _bools.Clear(); + _floats.Clear(); + _ints.Clear(); + //Create a parameter detail for each parameter that can be synchronized. + foreach (AnimatorControllerParameter item in _animator.parameters) + { + bool process = !_animator.IsParameterControlledByCurve(item.name); + + if (process) + { + //Over 250 parameters; who would do this!? + if (_parameterDetails.Count == 240) + { + Debug.LogError($"Parameter {item.name} exceeds the allowed 240 parameter count and is being ignored."); + continue; + } + + int typeIndex = 0; + //Bools. + if (item.type == AnimatorControllerParameterType.Bool) + { + typeIndex = _bools.Count; + _bools.Add(_animator.GetBool(item.nameHash)); + } + //Floats. + else if (item.type == AnimatorControllerParameterType.Float) + { + typeIndex = _floats.Count; + _floats.Add(_animator.GetFloat(item.name)); + } + //Ints. + else if (item.type == AnimatorControllerParameterType.Int) + { + typeIndex = _ints.Count; + _ints.Add(_animator.GetInteger(item.nameHash)); + } + //Triggers. + else if (item.type == AnimatorControllerParameterType.Trigger) + { + /* Triggers aren't persistent so they don't use stored values + * but I do need to make a parameter detail to track the hash. */ + typeIndex = -1; + } + + _parameterDetails.Add(new ParameterDetail(item, (byte)typeIndex)); + } + } + } + + /// + /// Sets which animator to use. You must call this with the appropriate animator on all clients and server. This change is not automatically synchronized. + /// + /// + public void SetAnimator(Animator animator) + { + //No update required. + if (animator == _lastAnimator) + return; + + _animator = animator; + InitializeOnce(); + _lastAnimator = animator; + } + + /// + /// Sets which controller to use. You must call this with the appropriate controller on all clients and server. This change is not automatically synchronized. + /// + /// + public void SetController(RuntimeAnimatorController controller) + { + //No update required. + if (controller == _lastController) + return; + + _animator.runtimeAnimatorController = controller; + InitializeOnce(); + _lastController = controller; + } + + /// + /// Checks to send animator data from server to clients. + /// + private void CheckSendToServer() + { + //Cannot send to server if is server or not client. + if (base.IsServer || !base.IsClient) + return; + //Cannot send to server if not client authoritative or don't have authority. + if (!ClientAuthoritative || !base.IsOwner) + return; + ////Not enough time passed to send. + //if (Time.time < _nextClientSendTime) + // return; + //_nextClientSendTime = Time.time + _synchronizeInterval; + + /* If there are updated parameters to send. + * Don't really need to worry about mtu here + * because there's no way the sent bytes are + * ever going to come close to the mtu + * when sending a single update. */ + if (AnimatorUpdated(out ArraySegment updatedBytes, _forceAllOnTimed)) + ServerAnimatorUpdated(updatedBytes); + + _forceAllOnTimed = false; + } + + /// + /// Checks to send animator data from server to clients. + /// + private void CheckSendToClients() + { + //Cannot send to clients if not server. + if (!base.IsServer) + return; + ////Not enough time passed to send. + //if (Time.time < _nextServerSendTime) + // return; + //_nextServerSendTime = Time.time + _synchronizeInterval; + + bool sendFromServer; + //If client authoritative. + if (ClientAuthoritative) + { + //If has no owner then use latest values on server. + if (!base.Owner.IsValid) + { + sendFromServer = true; + } + //If has a owner. + else + { + //If is owner then send latest values on server. + if (base.IsOwner) + { + sendFromServer = true; + } + //Not owner. + else + { + //Haven't received any data from clients, cannot send yet. + if (_clientAuthoritativeUpdates.BufferCount == 0) + { + return; + } + //Data was received from client; check eligibility to send it. + else + { + /* If forceAll is true then the latest values on + * server must be used, rather than what was received + * from client. This can occur if the client is possibly + * trying to use an attack or if the client is + * excessively sending updates. To prevent relaying that + * same data to others the server will send it's current + * animator settings in this scenario. */ + if (_clientAuthoritativeUpdates.ForceAll) + { + sendFromServer = true; + _clientAuthoritativeUpdates.Reset(); + } + else + { + sendFromServer = false; + } + } + } + } + } + //Not client authoritative, always send from server. + else + { + sendFromServer = true; + } + + /* If client authoritative then use what was received from clients + * if data exist. */ + if (!sendFromServer) + { + byte[] buffer = null; + int bufferLength = 0; + for (int i = 0; i < _clientAuthoritativeUpdates.BufferCount; i++) + { + _clientAuthoritativeUpdates.GetBuffer(i, ref buffer, ref bufferLength); + + //If null was returned then something went wrong. + if (buffer == null || bufferLength == 0) + continue; + + ObserversAnimatorUpdated(new ArraySegment(buffer, 0, bufferLength)); + } + //Reset client auth buffer. + _clientAuthoritativeUpdates.Reset(); + } + //Sending from server, send what's changed. + else + { + if (AnimatorUpdated(out ArraySegment updatedBytes, _forceAllOnTimed)) + ObserversAnimatorUpdated(updatedBytes); + + _forceAllOnTimed = false; + } + } + + + /// + /// Smooths floats on clients. + /// + private void SmoothFloats() + { + //Don't need to smooth on authoritative client. + if (!_canSmoothFloats) + return; + //Nothing to smooth. + if (_smoothedFloats.Count == 0) + return; + + float deltaTime = Time.deltaTime; + + List finishedEntries = new List(); + + /* Cycle through each target float and move towards it. + * Once at a target float mark it to be removed from floatTargets. */ + foreach (KeyValuePair item in _smoothedFloats) + { + float current = _animator.GetFloat(item.Key); + float next = Mathf.MoveTowards(current, item.Value.Target, item.Value.Rate * deltaTime); + _animator.SetFloat(item.Key, next); + + if (next == item.Value.Target) + finishedEntries.Add(item.Key); + } + + //Remove finished entries from dictionary. + for (int i = 0; i < finishedEntries.Count; i++) + _smoothedFloats.Remove(finishedEntries[i]); + } + + /// + /// Returns if animator is updated and bytes of updated values. + /// + /// + private bool AnimatorUpdated(out ArraySegment updatedBytes, bool forceAll = false) + { + updatedBytes = default; + //Something isn't setup right. + if (_layerWeights == null) + return false; + //Reset the writer. + _writer.Reset(); + + /* Every time a parameter is updated a byte is added + * for it's index, this is why requiredBytes increases + * by 1 when a value updates. ChangedParameter contains + * the index updated and the new value. The requiresBytes + * is increased also by however many bytes are required + * for the type which has changed. Some types use special parameter + * detail indexes, such as layer weights; these can be found under const. */ + for (byte parameterIndex = 0; parameterIndex < _parameterDetails.Count; parameterIndex++) + { + ParameterDetail pd = _parameterDetails[parameterIndex]; + /* Bool. */ + if (pd.ControllerParameter.type == AnimatorControllerParameterType.Bool) + { + bool next = _animator.GetBool(pd.Hash); + //If changed. + if (forceAll || _bools[pd.TypeIndex] != next) + { + _writer.WriteByte(parameterIndex); + _writer.WriteBoolean(next); + _bools[pd.TypeIndex] = next; + } + } + /* Float. */ + else if (pd.ControllerParameter.type == AnimatorControllerParameterType.Float) + { + float next = _animator.GetFloat(pd.Hash); + //If changed. + if (forceAll || _floats[pd.TypeIndex] != next) + { + _writer.WriteByte(parameterIndex); + _writer.WriteSingle(next, AutoPackType.Packed); + _floats[pd.TypeIndex] = next; + } + } + /* Int. */ + else if (pd.ControllerParameter.type == AnimatorControllerParameterType.Int) + { + int next = _animator.GetInteger(pd.Hash); + //If changed. + if (forceAll || _ints[pd.TypeIndex] != next) + { + _writer.WriteByte(parameterIndex); + _writer.WriteInt32(next, AutoPackType.Packed); + _ints[pd.TypeIndex] = next; + } + } + } + + /* Don't need to force trigger sends since + * they're one-shots. */ + for (int i = 0; i < _triggerUpdates.Count; i++) + { + _writer.WriteByte(_triggerUpdates[i].ParameterIndex); + _writer.WriteBoolean(_triggerUpdates[i].Setting); + } + _triggerUpdates.Clear(); + + /* States. */ + if (forceAll) + { + //Add all layers to layer states. + for (int i = 0; i < _animator.layerCount; i++) + _unsynchronizedLayerStates[i] = new StateChange(); + } + + //Go through each layer which needs to be synchronized. + foreach (KeyValuePair item in _unsynchronizedLayerStates) + { + int layerIndex = item.Key; + StateChange sc = item.Value; + //If a regular state change. + if (!sc.IsCrossfade) + { + if (ReturnCurrentLayerState(out int stateHash, out float normalizedTime, layerIndex)) + { + _writer.WriteByte(STATE); + _writer.WriteByte((byte)layerIndex); + //Current hash will always be too large to compress. + _writer.WriteInt32(stateHash); + _writer.WriteSingle(normalizedTime, AutoPackType.Packed); + } + } + //When it's a crossfade then send crossfade data. + else + { + _writer.WriteByte(CROSSFADE); + _writer.WriteByte((byte)layerIndex); + //Current hash will always be too large to compress. + _writer.WriteInt32(sc.Hash); + _writer.WriteBoolean(sc.FixedTime); + //Times usually can be compressed. + _writer.WriteSingle(sc.DurationTime, AutoPackType.Packed); + _writer.WriteSingle(sc.OffsetTime, AutoPackType.Packed); + _writer.WriteSingle(sc.NormalizedTransitionTime, AutoPackType.Packed); + } + } + _unsynchronizedLayerStates.Clear(); + + /* Layer weights. */ + for (int layerIndex = 0; layerIndex < _layerWeights.Length; layerIndex++) + { + float next = _animator.GetLayerWeight(layerIndex); + if (forceAll || _layerWeights[layerIndex] != next) + { + _writer.WriteByte(LAYER_WEIGHT); + _writer.WriteByte((byte)layerIndex); + _writer.WriteSingle(next, AutoPackType.Packed); + _layerWeights[layerIndex] = next; + } + } + + /* Speed is similar to layer weights but we don't need the index, + * only the indicator and value. */ + float speedNext = _animator.speed; + if (forceAll || _speed != speedNext) + { + _writer.WriteByte(SPEED); + _writer.WriteSingle(speedNext, AutoPackType.Packed); + _speed = speedNext; + } + + //Nothing to update. + if (_writer.Position == 0) + { + return false; + } + else + { + updatedBytes = _writer.GetArraySegment(); + return true; + } + } + + /// + /// Applies changed parameters to the animator. + /// + /// + private void ApplyParametersUpdated(ref ArraySegment updatedParameters) + { + if (!_isAnimatorEnabled) + return; + if (_layerWeights == null) + return; + if (updatedParameters.Count == 0) + return; + //Exit if client authoritative and has authority. + if (ClientAuthoritative && base.IsOwner) + return; + //Exit if not client authoritative, but also not sync to owner, and is owner. + if (!ClientAuthoritative && !_sendToOwner && base.IsOwner) + return; + //Exit if trying to apply when server and not client authoritative. + if (base.IsServer && !ClientAuthoritative) + return; + + try + { + using (PooledReader reader = ReaderPool.GetReader(updatedParameters, base.NetworkManager)) + { + while (reader.Remaining > 0) + { + byte parameterIndex = reader.ReadByte(); + //Layer weight + if (parameterIndex == LAYER_WEIGHT) + { + byte layerIndex = reader.ReadByte(); + float value = reader.ReadSingle(AutoPackType.Packed); + _animator.SetLayerWeight((int)layerIndex, value); + } + //Speed. + else if (parameterIndex == SPEED) + { + float value = reader.ReadSingle(AutoPackType.Packed); + _animator.speed = value; + } + //State. + else if (parameterIndex == STATE) + { + byte layerIndex = reader.ReadByte(); + //Hashes will always be too large to compress. + int hash = reader.ReadInt32(); + float normalizedTime = reader.ReadSingle(AutoPackType.Packed); + //Play results. + _animator.Play(hash, layerIndex, normalizedTime); + } + //Crossfade. + else if (parameterIndex == CROSSFADE) + { + byte layerIndex = reader.ReadByte(); + //Hashes will always be too large to compress. + int hash = reader.ReadInt32(); + bool useFixedTime = reader.ReadBoolean(); + //Get time values. + float durationTime = reader.ReadSingle(AutoPackType.Packed); + float offsetTime = reader.ReadSingle(AutoPackType.Packed); + float normalizedTransitionTime = reader.ReadSingle(AutoPackType.Packed); + //If using fixed. + if (useFixedTime) + _animator.CrossFadeInFixedTime(hash, durationTime, layerIndex, offsetTime, normalizedTransitionTime); + else + _animator.CrossFade(hash, durationTime, layerIndex, offsetTime, normalizedTransitionTime); + } + //Not a predetermined index, is an actual parameter. + else + { + AnimatorControllerParameterType acpt = _parameterDetails[parameterIndex].ControllerParameter.type; + if (acpt == AnimatorControllerParameterType.Bool) + { + bool value = reader.ReadBoolean(); + _animator.SetBool(_parameterDetails[parameterIndex].Hash, value); + } + //Float. + else if (acpt == AnimatorControllerParameterType.Float) + { + float value = reader.ReadSingle(AutoPackType.Packed); + //If able to smooth floats. + if (_canSmoothFloats) + { + float currentValue = _animator.GetFloat(_parameterDetails[parameterIndex].Hash); + float past = (float)base.TimeManager.TickDelta; + //float past = _synchronizeInterval + INTERPOLATION; + float rate = Mathf.Abs(currentValue - value) / past; + _smoothedFloats[_parameterDetails[parameterIndex].Hash] = new SmoothedFloat(rate, value); + } + else + { + _animator.SetFloat(_parameterDetails[parameterIndex].Hash, value); + } + } + //Integer. + else if (acpt == AnimatorControllerParameterType.Int) + { + int value = reader.ReadInt32(); + _animator.SetInteger(_parameterDetails[parameterIndex].Hash, value); + } + //Trigger. + else if (acpt == AnimatorControllerParameterType.Trigger) + { + bool value = reader.ReadBoolean(); + if (value) + _animator.SetTrigger(_parameterDetails[parameterIndex].Hash); + else + _animator.ResetTrigger(_parameterDetails[parameterIndex].Hash); + } + //Unhandled. + else + { + Debug.LogWarning($"Unhandled parameter type of {acpt}."); + } + } + } + } + } + catch + { + Debug.LogWarning("An error occurred while applying updates. This may occur when malformed data is sent or when you change the animator or controller but not on all connections."); + } + } + + /// + /// Outputs the current state and time for a layer. Returns true if stateHash is not 0. + /// + /// + /// + /// + /// + /// + private bool ReturnCurrentLayerState(out int stateHash, out float normalizedTime, int layerIndex) + { + stateHash = 0; + normalizedTime = 0f; + + if (!_isAnimatorEnabled) + return false; + + AnimatorStateInfo st = _animator.GetCurrentAnimatorStateInfo(layerIndex); + stateHash = st.fullPathHash; + normalizedTime = st.normalizedTime; + + return (stateHash != 0); + } + + /// + /// Forces values to send next update regardless of time remaining. + /// Can be useful if you have a short lasting parameter that you want to ensure goes through. + /// + public void ForceSend() + { + //_nextClientSendTime = 0f; + //_nextServerSendTime = 0f; + } + + /// + /// Immediately sends all variables and states of layers. + /// This is a very bandwidth intensive operation. + /// + public void SendAll() + { + _forceAllOnTimed = true; + ForceSend(); + } + + #region Play. + /// + /// Plays a state. + /// + public void Play(string name) + { + Play(Animator.StringToHash(name)); + } + /// + /// Plays a state. + /// + public void Play(int hash) + { + for (int i = 0; i < _animator.layerCount; i++) + Play(hash, i, 0f); + } + /// + /// Plays a state. + /// + public void Play(string name, int layer) + { + Play(Animator.StringToHash(name), layer); + } + /// + /// Plays a state. + /// + public void Play(int hash, int layer) + { + Play(hash, layer, 0f); + } + /// + /// Plays a state. + /// + public void Play(string name, int layer, float normalizedTime) + { + Play(Animator.StringToHash(name), layer, normalizedTime); + } + /// + /// Plays a state. + /// + public void Play(int hash, int layer, float normalizedTime) + { + if (!_isAnimatorEnabled) + return; + if (_animator.HasState(layer, hash)) + { + _animator.Play(hash, layer, normalizedTime); + _unsynchronizedLayerStates[layer] = new StateChange(); + } + } + /// + /// Plays a state. + /// + public void PlayInFixedTime(string name, float fixedTime) + { + PlayInFixedTime(Animator.StringToHash(name), fixedTime); + } + /// + /// Plays a state. + /// + public void PlayInFixedTime(int hash, float fixedTime) + { + for (int i = 0; i < _animator.layerCount; i++) + PlayInFixedTime(hash, i, fixedTime); + } + /// + /// Plays a state. + /// + public void PlayInFixedTime(string name, int layer, float fixedTime) + { + PlayInFixedTime(Animator.StringToHash(name), layer, fixedTime); + } + /// + /// Plays a state. + /// + public void PlayInFixedTime(int hash, int layer, float fixedTime) + { + if (!_isAnimatorEnabled) + return; + if (_animator.HasState(layer, hash)) + { + _animator.PlayInFixedTime(hash, layer, fixedTime); + _unsynchronizedLayerStates[layer] = new StateChange(); + } + } + #endregion + + #region Crossfade. + /// + /// Creates a crossfade from the current state to any other state using normalized times. + /// + /// + /// + /// + /// + /// + public void CrossFade(string stateName, float normalizedTransitionDuration, int layer, float normalizedTimeOffset = float.NegativeInfinity, float normalizedTransitionTime = 0.0f) + { + CrossFade(Animator.StringToHash(stateName), normalizedTransitionDuration, layer, normalizedTimeOffset, normalizedTransitionTime); + } + /// + /// Creates a crossfade from the current state to any other state using normalized times. + /// + /// + /// + /// + /// + /// + public void CrossFade(int hash, float normalizedTransitionDuration, int layer, float normalizedTimeOffset = 0.0f, float normalizedTransitionTime = 0.0f) + { + if (!_isAnimatorEnabled) + return; + if (_animator.HasState(layer, hash)) + { + _animator.CrossFade(hash, normalizedTransitionDuration, layer, normalizedTimeOffset, normalizedTransitionTime); + _unsynchronizedLayerStates[layer] = new StateChange(hash, false, normalizedTransitionDuration, normalizedTimeOffset, normalizedTransitionTime); + } + } + /// + /// Creates a crossfade from the current state to any other state using times in seconds. + /// + /// + /// + /// + /// + /// + public void CrossFadeInFixedTime(string stateName, float fixedTransitionDuration, int layer, float fixedTimeOffset = 0.0f, float normalizedTransitionTime = 0.0f) + { + CrossFadeInFixedTime(Animator.StringToHash(stateName), fixedTransitionDuration, layer, fixedTimeOffset, normalizedTransitionTime); + } + /// + /// Creates a crossfade from the current state to any other state using times in seconds. + /// + /// + /// + /// + /// + /// + public void CrossFadeInFixedTime(int hash, float fixedTransitionDuration, int layer, float fixedTimeOffset = 0.0f, float normalizedTransitionTime = 0.0f) + { + if (!_isAnimatorEnabled) + return; + if (_animator.HasState(layer, hash)) + { + _animator.CrossFadeInFixedTime(hash, fixedTransitionDuration, layer, fixedTimeOffset, normalizedTransitionTime); + _unsynchronizedLayerStates[layer] = new StateChange(hash, true, fixedTransitionDuration, fixedTimeOffset, normalizedTransitionTime); + } + } + #endregion + + #region Triggers. + /// + /// Sets a trigger on the animator and sends it over the network. + /// + /// + public void SetTrigger(int hash) + { + if (!_isAnimatorEnabled) + return; + UpdateTrigger(hash, true); + } + /// + /// Sets a trigger on the animator and sends it over the network. + /// + /// + public void SetTrigger(string name) + { + SetTrigger(Animator.StringToHash(name)); + } + + /// + /// Resets a trigger on the animator and sends it over the network. + /// + /// + public void ResetTrigger(int hash) + { + UpdateTrigger(hash, false); + } + /// + /// Resets a trigger on the animator and sends it over the network. + /// + /// + public void ResetTrigger(string name) + { + ResetTrigger(Animator.StringToHash(name)); + } + + /// + /// Updates a trigger, sets or resets. + /// + /// + private void UpdateTrigger(int hash, bool set) + { + if (!_isAnimatorEnabled) + return; + /* Allow triggers to run on owning client if using client authority, + * as well when not using client authority but also not using synchronize to owner. + * This allows clients to run animations locally while maintaining server authority. */ + //Using client authority but not owner. + if (ClientAuthoritative && !base.IsOwner) + return; + + //Also block if not using client authority, synchronizing to owner, and not server. + if (!ClientAuthoritative && _sendToOwner && !base.IsServer) + return; + + //Update locally. + if (set) + _animator.SetTrigger(hash); + else + _animator.ResetTrigger(hash); + + /* Can send if not client auth but is server, + * or if client auth and owner. */ + bool canSend = (!ClientAuthoritative && base.IsServer) || + (ClientAuthoritative && base.IsOwner); + //Only queue a send if proper side. + if (canSend) + { + for (byte i = 0; i < _parameterDetails.Count; i++) + { + if (_parameterDetails[i].Hash == hash) + { + _triggerUpdates.Add(new TriggerUpdate(i, set)); + return; + } + } + //Fall through, hash not found. + Debug.LogWarning($"Hash {hash} not found while trying to update a trigger."); + } + } + #endregion + + #region Remote actions. + /// + /// Called on clients to receive an animator update. + /// + /// + [ObserversRpc] + private void ObserversAnimatorUpdated(ArraySegment data) + { + ServerDataReceived(ref data); + } + /// + /// Called on clients to receive an animator update. + /// + /// + [TargetRpc] + private void TargetAnimatorUpdated(NetworkConnection connection, ArraySegment data) + { + ServerDataReceived(ref data); + } + /// + /// Called on server to receive an animator update. + /// + /// + [ServerRpc] + private void ServerAnimatorUpdated(ArraySegment data) + { + if (!_isAnimatorEnabled) + return; + if (!ClientAuthoritative) + { + base.Owner.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"Connection Id {base.Owner.ClientId} has been kicked for trying to update this object without client authority."); + return; + } + + /* Server does not need to apply interpolation. + * Even as clientHost when CSP is being used the + * clientHost will always be on the latest tick. + * Spectators on the other hand will remain behind + * a little depending on their components interpolation. */ + ApplyParametersUpdated(ref data); + _clientAuthoritativeUpdates.AddToBuffer(ref data); + } + /// + /// Called on clients to receive an animator update. + /// + /// + private void ServerDataReceived(ref ArraySegment data) + { + if (!_isAnimatorEnabled) + return; + //If also server, client host, then do nothing. Animations already ran on server. + if (base.IsServer) + return; + + //If has authority. + if (base.IsOwner) + { + //No need to sync to self if client authoritative. + if (ClientAuthoritative) + return; + //Not client authoritative, but also don't sync to owner. + else if (!ClientAuthoritative && !_sendToOwner) + return; + } + + ReceivedServerData rd = new ReceivedServerData(data); + _fromServerBuffer.Enqueue(rd); + + if (_startTick == uint.MaxValue) + _startTick = (base.TimeManager.LocalTick + _interpolation); + //ApplyParametersUpdated(ref data); + } + #endregion + + #region Editor. +#if UNITY_EDITOR + protected override void Reset() + { + base.Reset(); + if (_animator == null) + SetAnimator(GetComponent()); + } +#endif + #endregion + + } +} + diff --git a/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/NetworkAnimator.cs.meta b/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/NetworkAnimator.cs.meta new file mode 100644 index 0000000..0d7e115 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/NetworkAnimator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e8cac635f24954048aad3a6ff9110beb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/NetworkTransform.meta b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform.meta new file mode 100644 index 0000000..c0513ca --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 63eb855bc9013d54d9ea73088d204790 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/Editor.meta b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/Editor.meta new file mode 100644 index 0000000..de2183e --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6c1769e2b4cabd744a4b875f6849ef76 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/Editor/NetworkTransformEditor.cs b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/Editor/NetworkTransformEditor.cs new file mode 100644 index 0000000..13227ae --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/Editor/NetworkTransformEditor.cs @@ -0,0 +1,134 @@ +#if UNITY_EDITOR +using FishNet.Editing; +using UnityEditor; +using UnityEngine; + +namespace FishNet.Component.Transforming.Editing +{ + + + [CustomEditor(typeof(NetworkTransform), true)] + [CanEditMultipleObjects] + public class NetworkTransformEditor : Editor + { + private SerializedProperty _componentConfiguration; + private SerializedProperty _synchronizeParent; + private SerializedProperty _packing; + private SerializedProperty _interpolation; + private SerializedProperty _extrapolation; + private SerializedProperty _enableTeleport; + private SerializedProperty _teleportThreshold; + private SerializedProperty _clientAuthoritative; + private SerializedProperty _sendToOwner; + private SerializedProperty _synchronizePosition; + private SerializedProperty _positionSnapping; + private SerializedProperty _synchronizeRotation; + private SerializedProperty _rotationSnapping; + private SerializedProperty _synchronizeScale; + private SerializedProperty _scaleSnapping; + + + protected virtual void OnEnable() + { + _componentConfiguration = serializedObject.FindProperty(nameof(_componentConfiguration)); + _synchronizeParent = serializedObject.FindProperty("_synchronizeParent"); + _packing = serializedObject.FindProperty("_packing"); + _interpolation = serializedObject.FindProperty("_interpolation"); + _extrapolation = serializedObject.FindProperty("_extrapolation"); + _enableTeleport = serializedObject.FindProperty("_enableTeleport"); + _teleportThreshold = serializedObject.FindProperty("_teleportThreshold"); + _clientAuthoritative = serializedObject.FindProperty("_clientAuthoritative"); + _sendToOwner = serializedObject.FindProperty("_sendToOwner"); + _synchronizePosition = serializedObject.FindProperty("_synchronizePosition"); + _positionSnapping = serializedObject.FindProperty("_positionSnapping"); + _synchronizeRotation = serializedObject.FindProperty("_synchronizeRotation"); + _rotationSnapping = serializedObject.FindProperty("_rotationSnapping"); + _synchronizeScale = serializedObject.FindProperty("_synchronizeScale"); + _scaleSnapping = serializedObject.FindProperty("_scaleSnapping"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + GUI.enabled = false; + EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour((NetworkTransform)target), typeof(NetworkTransform), false); + GUI.enabled = true; + + +#pragma warning disable CS0162 // Unreachable code detected + EditorGUILayout.HelpBox(EditingConstants.PRO_ASSETS_LOCKED_TEXT, MessageType.Warning); +#pragma warning restore CS0162 // Unreachable code detected + + //Misc. + EditorGUILayout.LabelField("Misc", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_componentConfiguration); + EditorGUILayout.PropertyField(_synchronizeParent, new GUIContent("* Synchronize Parent")); + EditorGUILayout.PropertyField(_packing); + EditorGUI.indentLevel--; + EditorGUILayout.Space(); + + //Smoothing. + EditorGUILayout.LabelField("Smoothing", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_interpolation); + EditorGUILayout.PropertyField(_extrapolation, new GUIContent("* Extrapolation")); + EditorGUILayout.PropertyField(_enableTeleport); + if (_enableTeleport.boolValue) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_teleportThreshold); + EditorGUI.indentLevel--; + } + EditorGUI.indentLevel--; + EditorGUILayout.Space(); + + //Authority. + EditorGUILayout.LabelField("Authority", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_clientAuthoritative); + if (!_clientAuthoritative.boolValue) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_sendToOwner); + EditorGUI.indentLevel--; + } + EditorGUI.indentLevel--; + EditorGUILayout.Space(); + + //Synchronizing. + EditorGUILayout.LabelField("Synchronizing.", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + //Position. + EditorGUILayout.PropertyField(_synchronizePosition); + if (_synchronizePosition.boolValue) + { + EditorGUI.indentLevel += 2; + EditorGUILayout.PropertyField(_positionSnapping); + EditorGUI.indentLevel -= 2; + } + //Rotation. + EditorGUILayout.PropertyField(_synchronizeRotation); + if (_synchronizeRotation.boolValue) + { + EditorGUI.indentLevel += 2; + EditorGUILayout.PropertyField(_rotationSnapping); + EditorGUI.indentLevel -= 2; + } + //Scale. + EditorGUILayout.PropertyField(_synchronizeScale); + if (_synchronizeScale.boolValue) + { + EditorGUI.indentLevel += 2; + EditorGUILayout.PropertyField(_scaleSnapping); + EditorGUI.indentLevel -= 2; + } + EditorGUI.indentLevel--; + + serializedObject.ApplyModifiedProperties(); + } + } + +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/Editor/NetworkTransformEditor.cs.meta b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/Editor/NetworkTransformEditor.cs.meta new file mode 100644 index 0000000..7c5a2ca --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/Editor/NetworkTransformEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3ed56c899b8ecf241a2bc3e40a2dabc1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/NetworkTransform.cs b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/NetworkTransform.cs new file mode 100644 index 0000000..aa37436 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/NetworkTransform.cs @@ -0,0 +1,2042 @@ +#if UNITY_EDITOR || DEVELOPMENT_BUILD +#define DEVELOPMENT +#endif +using FishNet.Connection; +using FishNet.Documenting; +using FishNet.Managing.Logging; +using FishNet.Managing.Server; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Transporting; +using FishNet.Utility.Extension; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Component.Transforming +{ + [DisallowMultipleComponent] + [AddComponentMenu("FishNet/Component/NetworkTransform")] + public sealed class NetworkTransform : NetworkBehaviour + { + #region Types. + [System.Serializable] + public enum ComponentConfigurationType + { + Disabled = 0, + CharacterController = 1, + Rigidbody = 2, + Rigidbody2D = 3, + } + private struct ReceivedData + { + public List HasData; + public PooledWriter Writer; + public Channel Channel; + + /// + /// Sets has data value for all LODs. + /// + public void SetHasData(bool value) + { + for (int i = 0; i < HasData.Count; i++) + HasData[i] = value; + } + /// + /// Sets the data is available for a single LOD. + /// + /// + public void SetHasData(bool value, byte index) + { + if (index >= HasData.Count) + return; + + HasData[index] = value; + } + } + + [System.Serializable] + public struct SnappedAxes + { + public bool X; + public bool Y; + public bool Z; + } + private enum ChangedDelta + { + Unset = 0, + PositionX = 1, + PositionY = 2, + PositionZ = 4, + Rotation = 8, + Extended = 16, + ScaleX = 32, + ScaleY = 64, + ScaleZ = 128, + Nested = 256 + } + private enum ChangedFull + { + Unset = 0, + Position = 1, + Rotation = 2, + Scale = 4, + Nested = 8 + } + + private enum UpdateFlagA : byte + { + Unset = 0, + X2 = 1, + X4 = 2, + Y2 = 4, + Y4 = 8, + Z2 = 16, + Z4 = 32, + Rotation = 64, + Extended = 128 + } + private enum UpdateFlagB : byte + { + Unset = 0, + X2 = 1, + X4 = 2, + Y2 = 4, + Y4 = 8, + Z2 = 16, + Z4 = 32, + Nested = 64 + } + public class GoalData + { + public uint ReceivedTick; + public RateData Rates = new RateData(); + public TransformData Transforms = new TransformData(); + + public GoalData() { } + public void Reset() + { + ReceivedTick = 0; + Transforms.Reset(); + Rates.Reset(); + } + } + public class RateData + { + /// + /// Rate for position after smart calculations. + /// + public float Position; + /// + /// Rate for rotation after smart calculations. + /// + public float Rotation; + /// + /// Rate for scale after smart calculations. + /// + public float Scale; + /// + /// Unaltered rate for position calculated through position change and tickspan. + /// + public float LastUnalteredPositionRate; + /// + /// Number of ticks the rates are calculated for. + /// If TickSpan is 2 then the rates are calculated under the assumption the transform changed over 2 ticks. + /// + public uint TickSpan; + /// + /// True if the rate is believed to be fluctuating unusually. + /// + internal bool AbnormalRateDetected; + /// + /// Time remaining until transform is expected to reach it's goal. + /// + internal float TimeRemaining; + + public RateData() { } + + public void Reset() + { + Position = 0f; + Rotation = 0f; + Scale = 0f; + LastUnalteredPositionRate = 0f; + TickSpan = 0; + AbnormalRateDetected = false; + TimeRemaining = 0f; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Update(RateData rd) + { + Update(rd.Position, rd.Rotation, rd.Scale, rd.LastUnalteredPositionRate, rd.TickSpan, rd.AbnormalRateDetected, rd.TimeRemaining); + } + + /// + /// Updates rates. + /// + public void Update(float position, float rotation, float scale, float unalteredPositionRate, uint tickSpan, bool abnormalRateDetected, float timeRemaining) + { + Position = position; + Rotation = rotation; + Scale = scale; + LastUnalteredPositionRate = unalteredPositionRate; + TickSpan = tickSpan; + AbnormalRateDetected = abnormalRateDetected; + TimeRemaining = timeRemaining; + } + } + + public class TransformData + { + public enum ExtrapolateState : byte + { + Disabled = 0, + Available = 1, + Active = 2 + } + public uint Tick; + public bool Snapped; + public Vector3 Position; + public Quaternion Rotation; + public Vector3 Scale; + public Vector3 ExtrapolatedPosition; + public ExtrapolateState ExtrapolationState; + public NetworkBehaviour ParentBehaviour; + public TransformData() { } + + internal void Reset() + { + Tick = 0; + Snapped = false; + Position = Vector3.zero; + Rotation = Quaternion.identity; + Scale = Vector3.zero; + ExtrapolatedPosition = Vector3.zero; + ExtrapolationState = ExtrapolateState.Disabled; + ParentBehaviour = null; + } + internal void Update(TransformData copy) + { + Update(copy.Tick, copy.Position, copy.Rotation, copy.Scale, copy.ExtrapolatedPosition, copy.ParentBehaviour); + } + internal void Update(uint tick, Vector3 position, Quaternion rotation, Vector3 scale, Vector3 extrapolatedPosition, NetworkBehaviour parentBehaviour) + { + Tick = tick; + Position = position; + Rotation = rotation; + Scale = scale; + ExtrapolatedPosition = extrapolatedPosition; + ParentBehaviour = parentBehaviour; + } + } + + #endregion + + #region Public. + /// + /// + /// + /// + /// + [APIExclude] + public delegate void DataReceivedChanged(TransformData prev, TransformData next); + /// + /// Called when new data is received. Previous and next data are provided. Next data may be manipulated. + /// + public event DataReceivedChanged OnDataReceived; + /// + /// Called when GoalData is updated. + /// + public event Action OnNextGoal; + /// + /// Called when the transform has reached it's goal. + /// + public event Action OnInterpolationComplete; + /// + /// True if the local client used TakeOwnership and is awaiting an ownership change. + /// + public bool TakenOwnership { get; private set; } + #endregion + + #region Serialized. + /// + /// Attached movement component to automatically configure. + /// + [Tooltip("Attached movement component to automatically configure.")] + [SerializeField] + private ComponentConfigurationType _componentConfiguration = ComponentConfigurationType.Disabled; + /// + /// True to synchronize when this transform changes parent. + /// + [Tooltip("True to synchronize when this transform changes parent.")] + [SerializeField] + private bool _synchronizeParent; + /// + /// How much to compress each transform property. + /// + [Tooltip("How much to compress each transform property.")] + [SerializeField] + private TransformPackingData _packing = new TransformPackingData() + { + Position = AutoPackType.Packed, + Rotation = AutoPackType.Packed, + Scale = AutoPackType.Unpacked + }; + /// + /// How many ticks to interpolate. + /// + [Tooltip("How many ticks to interpolate.")] + [Range(1, MAX_INTERPOLATION)] + [SerializeField] + private ushort _interpolation = 2; + /// + /// How many ticks to extrapolate. + /// + [Tooltip("How many ticks to extrapolate.")] + [Range(0, 1024)] + [SerializeField] +#pragma warning disable CS0414 //Not in use. + private ushort _extrapolation = 2; +#pragma warning restore CS0414 //Not in use. + /// + /// True to enable teleport threshhold. + /// + [Tooltip("True to enable teleport threshhold.")] + [SerializeField] + private bool _enableTeleport; + /// + /// How far the transform must travel in a single update to cause a teleport rather than smoothing. Using 0f will teleport every update. + /// + [Tooltip("How far the transform must travel in a single update to cause a teleport rather than smoothing. Using 0f will teleport every update.")] + [Range(0f, float.MaxValue)] + [SerializeField] + private float _teleportThreshold = 1f; + /// + /// True if owner controls how the object is synchronized. + /// + [Tooltip("True if owner controls how the object is synchronized.")] + [SerializeField] + private bool _clientAuthoritative = true; + /// + /// True to synchronize movements on server to owner when not using client authoritative movement. + /// + [Tooltip("True to synchronize movements on server to owner when not using client authoritative movement.")] + [SerializeField] + private bool _sendToOwner = true; + /// + /// Gets SendToOwner. + /// + public bool GetSendToOwner() => _sendToOwner; + /// + /// Sets SendToOwner. Only the server may call this method. + /// + /// New value. + public void SetSendToOwner(bool value) + { + _sendToOwner = value; + if (base.IsServer) + ObserversSetSendToOwner(value); + } + /// + /// How often in ticks to synchronize. This is default to 1 but will change depending on LOD to ensure proper smoothing calculations + /// + private byte _interval = 1; + /// + /// True to synchronize position. Even while checked only changed values are sent. + /// + [Tooltip("True to synchronize position. Even while checked only changed values are sent.")] + [SerializeField] + private bool _synchronizePosition = true; + /// + /// Sets if to synchronize position. + /// + /// New value. + public void SetSynchronizePosition(bool value) => _synchronizePosition = value; + /// + /// Axes to snap on position. + /// + [Tooltip("Axes to snap on position.")] + [SerializeField] + private SnappedAxes _positionSnapping = new SnappedAxes(); + /// + /// Sets which Position axes to snap. + /// + /// Axes to snap. + public void SetPositionSnapping(SnappedAxes axes) => _positionSnapping = axes; + /// + /// True to synchronize rotation. Even while checked only changed values are sent. + /// + [Tooltip("True to synchronize rotation. Even while checked only changed values are sent.")] + [SerializeField] + private bool _synchronizeRotation = true; + /// + /// Sets if to synchronize rotation. + /// + /// New value. + public void SetSynchronizeRotation(bool value) => _synchronizeRotation = value; + /// + /// Axes to snap on rotation. + /// + [Tooltip("Axes to snap on rotation.")] + [SerializeField] + private SnappedAxes _rotationSnapping = new SnappedAxes(); + /// + /// Sets which Scale axes to snap. + /// + /// Axes to snap. + public void SetRotationSnapping(SnappedAxes axes) => _rotationSnapping = axes; + /// + /// True to synchronize scale. Even while checked only changed values are sent. + /// + [Tooltip("True to synchronize scale. Even while checked only changed values are sent.")] + [SerializeField] + private bool _synchronizeScale = true; + /// + /// Sets if to synchronize scale. + /// + /// New value. + public void SetSynchronizeScale(bool value) => _synchronizeScale = value; + /// + /// Axes to snap on scale. + /// + [Tooltip("Axes to snap on scale.")] + [SerializeField] + private SnappedAxes _scaleSnapping = new SnappedAxes(); + /// + /// Sets which Scale axes to snap. + /// + /// Axes to snap. + public void SetScaleSnapping(SnappedAxes axes) => _scaleSnapping = axes; + #endregion + + #region Private. + /// + /// Packing data with all values set to uncompressed. + /// + private TransformPackingData _unpacked = new TransformPackingData() + { + Position = AutoPackType.Unpacked, + Rotation = AutoPackType.Unpacked, + Scale = AutoPackType.Unpacked + }; + /// + /// True if the last DataReceived was on the reliable channel. Default to true so initial values do not extrapolate. + /// + private bool _lastReceiveReliable = true; + /// + /// NetworkBehaviour this transform is a child of. + /// + private NetworkBehaviour _parentBehaviour; + /// + /// Last transform which this object was a child of. + /// + private Transform _parentTransform; + /// + /// Values changed over time that server has sent to clients since last reliable has been sent. + /// + private List _serverChangedSinceReliable = new List(); + /// + /// Values changed over time that client has sent to server since last reliable has been sent. + /// + private ChangedDelta _clientChangedSinceReliable = ChangedDelta.Unset; + /// + /// Last tick an ObserverRpc passed checks. + /// + private uint _lastObserversRpcTick; + /// + /// Last tick a ServerRpc passed checks. + /// + private uint _lastServerRpcTick; + /// + /// Last received data from an authoritative client. + /// + private ReceivedData _receivedClientData = new ReceivedData(); + /// + /// True if subscribed to TimeManager for ticks. + /// + private bool _subscribedToTicks; + /// + /// Last TransformData to be received. + /// + private TransformData _lastReceivedTransformData = new TransformData(); + /// + /// Last RateData to be calculated from LastReceivedTransformData. + /// + private RateData _lastCalculatedRateData = new RateData(); + /// + /// GoalDatas to move towards. + /// + private Queue _goalDataQueue = new Queue(); + /// + /// Current GoalData being used. + /// + private GoalData _currentGoalData = new GoalData(); + /// + /// True if queue can be read. While true objects will move to CurrentGoalData. + /// + private bool _queueReady = false; + /// + /// Cache of GoalDatas to prevent allocations. + /// + private static Stack _goalDataCache = new Stack(); + /// + /// True if the transform has changed since it started. + /// + private bool _changedSinceStart; + /// + /// Last sent transform data for every LOD. + /// + private List _lastSentTransformDatas = new List(); + /// + /// Writers for changed data for each level of detail. + /// + private List _changedWriters = new List(); + #endregion + + #region Const. + /// + /// Maximum possible interpolation value. + /// + public const ushort MAX_INTERPOLATION = 250; + #endregion + + private void Awake() + { + _interval = Math.Max(_interval, (byte)1); + } + + private void OnDestroy() + { + if (_receivedClientData.Writer != null) + { + _receivedClientData.Writer.Dispose(); + _receivedClientData.Writer = null; + } + } + + public override void OnStartServer() + { + base.OnStartServer(); + + ConfigureComponents(); + _receivedClientData.HasData = new List(); + + //Initialize for LODs. + for (int i = 0; i < base.ObserverManager.GetLevelOfDetailDistances().Count; i++) + { + _changedWriters.Add(WriterPool.GetWriter()); + _lastSentTransformDatas.Add(new TransformData()); + _receivedClientData.HasData.Add(false); + _serverChangedSinceReliable.Add(ChangedDelta.Unset); + } + + SetDefaultGoalData(); + /* Server must always subscribe. + * Server needs to relay client auth in + * ticks or send non-auth/non-owner to + * clients in tick. */ + ChangeTickSubscription(true); + } + + public override void OnSpawnServer(NetworkConnection connection) + { + base.OnSpawnServer(connection); + /* If not on the root then the initial properties may need to be synchronized + * since the spawn message only sends root information. If initial + * properties have changed update spawning connection. */ + if (base.NetworkObject.gameObject != gameObject && _changedSinceStart) + { + //Send latest. + using (PooledWriter writer = WriterPool.GetWriter()) + { + ChangedDelta fullTransform = (ChangedDelta.PositionX | ChangedDelta.PositionY | ChangedDelta.PositionZ | ChangedDelta.Extended | ChangedDelta.ScaleX | ChangedDelta.ScaleY | ChangedDelta.ScaleZ | ChangedDelta.Rotation); + SerializeChanged(fullTransform, writer); + TargetUpdateTransform(connection, writer.GetArraySegment(), Channel.Reliable); + } + } + + + } + + public override void OnStartClient() + { + base.OnStartClient(); + ConfigureComponents(); + + //Initialize for LOD if client only. + if (base.IsClientOnly) + { + for (int i = 0; i < base.ObserverManager.GetLevelOfDetailDistances().Count; i++) + { + _changedWriters.Add(WriterPool.GetWriter()); + _lastSentTransformDatas.Add(new TransformData()); + } + } + + SetDefaultGoalData(); + } + + public override void OnOwnershipServer(NetworkConnection prevOwner) + { + base.OnOwnershipServer(prevOwner); + //Reset last tick since each client sends their own ticks. + _lastServerRpcTick = 0; + } + + public override void OnOwnershipClient(NetworkConnection prevOwner) + { + base.OnOwnershipClient(prevOwner); + + /* If newOwner is self then client + * must subscribe to ticks. Client can also + * unsubscribe from ticks if not owner, + * long as the server is also not active. */ + if (base.IsOwner) + { + ChangeTickSubscription(true); + } + //Not new owner. + else + { + /* If client authoritative and ownership was lost + * then default goals must be set to force the + * object to it's last transform. */ + if (_clientAuthoritative) + SetDefaultGoalData(); + + if (!base.IsServer) + ChangeTickSubscription(false); + } + } + + public override void OnStopServer() + { + base.OnStopServer(); + //Always unsubscribe; if the server stopped so did client. + ChangeTickSubscription(false); + for (int i = 0; i < base.ObserverManager.GetLevelOfDetailDistances().Count; i++) + { + _lastSentTransformDatas[i].Reset(); + _serverChangedSinceReliable[i] = ChangedDelta.Unset; + _receivedClientData.SetHasData(false, (byte)i); + } + } + + public override void OnStopClient() + { + base.OnStopClient(); + //If not also server unsubscribe from ticks. + if (!base.IsServer) + ChangeTickSubscription(false); + } + + private void Update() + { + MoveToTarget(); + } + + /// + /// Configures components automatically. + /// + private void ConfigureComponents() + { + //Disabled. + if (_componentConfiguration == ComponentConfigurationType.Disabled) + { + return; + } + //RB. + else if (_componentConfiguration == ComponentConfigurationType.Rigidbody) + { + + if (TryGetComponent(out Rigidbody c)) + { + bool isKinematic = (!base.IsOwner || base.IsServerOnly); + c.isKinematic = isKinematic; + c.interpolation = RigidbodyInterpolation.None; + } + } + //RB2D + else if (_componentConfiguration == ComponentConfigurationType.Rigidbody2D) + { + //Only client authoritative needs to be configured. + if (!_clientAuthoritative) + return; + if (TryGetComponent(out Rigidbody2D c)) + { + bool isKinematic = (!base.IsOwner || base.IsServerOnly); + c.isKinematic = isKinematic; + c.simulated = !isKinematic; + c.interpolation = RigidbodyInterpolation2D.None; + } + } + //CC + else if (_componentConfiguration == ComponentConfigurationType.CharacterController) + { + if (TryGetComponent(out CharacterController c)) + { + //Client auth. + if (_clientAuthoritative) + { + c.enabled = base.IsOwner; + } + //Server auth. + else + { + //Not CSP. + if (_sendToOwner) + c.enabled = base.IsServer; + //Most likely CSP. + else + c.enabled = (base.IsServer || base.IsOwner); + + } + } + } + } + + /// + /// Called when a tick occurs. + /// + private void TimeManager_OnPostTick() + { + + if (base.IsServer) + { + byte lodIndex = base.ObserverManager.LevelOfDetailIndex; + SendToClients(lodIndex); + } + + if (base.IsClient) + SendToServer(_lastSentTransformDatas[0]); + } + + /// + /// Tries to subscribe to TimeManager ticks. + /// + private void ChangeTickSubscription(bool subscribe) + { + if (subscribe == _subscribedToTicks) + return; + + _subscribedToTicks = subscribe; + if (subscribe) + base.NetworkManager.TimeManager.OnPostTick += TimeManager_OnPostTick; + else + base.NetworkManager.TimeManager.OnPostTick -= TimeManager_OnPostTick; + } + + /// + /// Sets SendToOwner value. + /// + /// + [ObserversRpc(BufferLast = true, ExcludeServer = true)] + private void ObserversSetSendToOwner(bool value) + { + _sendToOwner = value; + } + + /// + /// Resets last sent information to force a resend of current values. + /// + public void ForceSend() + { + for (int i = 0; i < _lastSentTransformDatas.Count; i++) + _lastSentTransformDatas[i].Reset(); + if (_receivedClientData.Writer != null) + _receivedClientData.SetHasData(true); + } + + /// + /// Updates the interval value over the network. + /// + /// New interval. + public void SetInterval(byte value) + { + bool canSet = (base.IsServer && !_clientAuthoritative) + || (base.IsServer && _clientAuthoritative && !base.Owner.IsValid) + || (_clientAuthoritative && base.IsOwner); + + if (!canSet) + return; + + if (base.IsServer) + ObserversSetInterval(value); + else + ServerSetInterval(value); + } + + /// + /// Updates the interval value. + /// + /// + private void SetIntervalInternal(byte value) + { + value = (byte)Mathf.Max(value, 1); + _interval = value; + } + + /// + /// Sets interval over the network. + /// + [ServerRpc(RunLocally = true)] + private void ServerSetInterval(byte value) + { + if (!_clientAuthoritative) + { + + base.Owner.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"Connection Id {base.Owner.ClientId} has been kicked for trying to update this object without client authority."); + return; + } + + SetIntervalInternal(value); + } + /// + /// Sets interval over the network. + /// + [ObserversRpc(RunLocally = true)] + private void ObserversSetInterval(byte value) + { + SetIntervalInternal(value); + } + + + /// + /// Creates goal data using current position. + /// + private void SetDefaultGoalData() + { + Transform t = transform; + NetworkBehaviour parentBehaviour = null; + //If there is a parent try to output the behaviour on it. + if (_synchronizeParent && transform.parent != null) + { + transform.parent.TryGetComponent(out parentBehaviour); + if (parentBehaviour == null) + { + LogInvalidParent(); + } + else + { + _parentTransform = transform.parent; + _parentBehaviour = parentBehaviour; + } + } + + _lastReceivedTransformData.Update(0, t.localPosition, t.localRotation, t.localScale, t.localPosition, parentBehaviour); + SetInstantRates(_currentGoalData.Rates); + } + + /// + /// Prints an invalid parent debug. + /// + private void LogInvalidParent() + { + Debug.LogWarning($"{gameObject.name} [Id {base.ObjectId}] is nested but the parent {transform.parent.name} does not contain a NetworkBehaviour component. To synchronize parents the parent object must have a NetworkBehaviour component, even if empty."); + } + + /// + /// Serializes only changed data into writer. + /// + /// + /// + private void SerializeChanged(ChangedDelta changed, PooledWriter writer) + { + UpdateFlagA flagsA = UpdateFlagA.Unset; + UpdateFlagB flagsB = UpdateFlagB.Unset; + /* Do not use compression when nested. Depending + * on the scale of the parent compression may + * not be accurate enough. */ + TransformPackingData packing = (ChangedContains(changed, ChangedDelta.Nested)) ? + _unpacked : _packing; + + int startIndexA = writer.Position; + writer.Reserve(1); + //Original axis value. + float original; + //Compressed axis value. + float compressed; + //Multiplier for compression. + float multiplier = 100f; + /* Maximum value compressed may be + * to send as compressed. */ + float maxValue = (short.MaxValue - 1); + + Transform t = transform; + /* Position. */ + if (_synchronizePosition) + { + AutoPackType localPacking = packing.Position; + //PositionX + if (ChangedContains(changed, ChangedDelta.PositionX)) + { + original = t.localPosition.x; + compressed = original * multiplier; + if (localPacking != AutoPackType.Unpacked && Math.Abs(compressed) <= maxValue) + { + flagsA |= UpdateFlagA.X2; + writer.WriteInt16((short)compressed); + } + else + { + flagsA |= UpdateFlagA.X4; + writer.WriteSingle(original); + } + } + //PositionY + if (ChangedContains(changed, ChangedDelta.PositionY)) + { + original = t.localPosition.y; + compressed = original * multiplier; + if (localPacking != AutoPackType.Unpacked && Math.Abs(compressed) <= maxValue) + { + flagsA |= UpdateFlagA.Y2; + writer.WriteInt16((short)compressed); + } + else + { + flagsA |= UpdateFlagA.Y4; + writer.WriteSingle(original); + } + } + //PositionZ + if (ChangedContains(changed, ChangedDelta.PositionZ)) + { + original = t.localPosition.z; + compressed = original * multiplier; + if (localPacking != AutoPackType.Unpacked && Math.Abs(compressed) <= maxValue) + { + flagsA |= UpdateFlagA.Z2; + writer.WriteInt16((short)compressed); + } + else + { + flagsA |= UpdateFlagA.Z4; + writer.WriteSingle(original); + } + } + } + + /* Rotation. */ + if (_synchronizeRotation) + { + if (ChangedContains(changed, ChangedDelta.Rotation)) + { + flagsA |= UpdateFlagA.Rotation; + /* Rotation can always use pack settings even + * if nested. Unsual transform scale shouldn't affect rotation. */ + writer.WriteQuaternion(t.localRotation, _packing.Rotation); + } + } + + if (ChangedContains(changed, ChangedDelta.Extended)) + { + AutoPackType localPacking = packing.Scale; + flagsA |= UpdateFlagA.Extended; + int startIndexB = writer.Position; + writer.Reserve(1); + + /* Scale. */ + if (_synchronizeScale) + { + //ScaleX + if (ChangedContains(changed, ChangedDelta.ScaleX)) + { + original = t.localScale.x; + compressed = original * multiplier; + if (localPacking != AutoPackType.Unpacked && Math.Abs(compressed) <= maxValue) + { + flagsB |= UpdateFlagB.X2; + writer.WriteInt16((short)compressed); + } + else + { + flagsB |= UpdateFlagB.X4; + writer.WriteSingle(original); + } + } + //ScaleY + if (ChangedContains(changed, ChangedDelta.ScaleY)) + { + original = t.localScale.y; + compressed = original * multiplier; + if (localPacking != AutoPackType.Unpacked && Math.Abs(compressed) <= maxValue) + { + flagsB |= UpdateFlagB.Y2; + writer.WriteInt16((short)compressed); + } + else + { + flagsB |= UpdateFlagB.Y4; + writer.WriteSingle(original); + } + } + //ScaleZ + if (ChangedContains(changed, ChangedDelta.ScaleZ)) + { + original = t.localScale.z; + compressed = original * multiplier; + if (localPacking != AutoPackType.Unpacked && Math.Abs(compressed) <= maxValue) + { + flagsB |= UpdateFlagB.Z2; + writer.WriteInt16((short)compressed); + } + else + { + flagsB |= UpdateFlagB.Z4; + writer.WriteSingle(original); + } + } + } + + //Nested. + if (ChangedContains(changed, ChangedDelta.Nested) && _parentBehaviour != null) + { + flagsB |= UpdateFlagB.Nested; + writer.WriteNetworkBehaviour(_parentBehaviour); + } + + writer.FastInsertByte((byte)flagsB, startIndexB); + } + + //Insert flags. + writer.FastInsertByte((byte)flagsA, startIndexA); + bool ChangedContains(ChangedDelta whole, ChangedDelta part) + { + return (whole & part) == part; + } + } + + /// + /// Deerializes a received packet. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DeserializePacket(ArraySegment data, TransformData prevTransformData, TransformData nextTransformData, ref ChangedFull changedFull) + { + using (PooledReader r = ReaderPool.GetReader(data, base.NetworkManager)) + { + UpdateFlagA flagsA = (UpdateFlagA)r.ReadByte(); + + int readerRemaining; + readerRemaining = r.Remaining; + //X + if (UpdateFlagAContains(flagsA, UpdateFlagA.X2)) + nextTransformData.Position.x = r.ReadInt16() / 100f; + else if (UpdateFlagAContains(flagsA, UpdateFlagA.X4)) + nextTransformData.Position.x = r.ReadSingle(); + else + nextTransformData.Position.x = prevTransformData.Position.x; + //Y + if (UpdateFlagAContains(flagsA, UpdateFlagA.Y2)) + nextTransformData.Position.y = r.ReadInt16() / 100f; + else if (UpdateFlagAContains(flagsA, UpdateFlagA.Y4)) + nextTransformData.Position.y = r.ReadSingle(); + else + nextTransformData.Position.y = prevTransformData.Position.y; + //Z + if (UpdateFlagAContains(flagsA, UpdateFlagA.Z2)) + nextTransformData.Position.z = r.ReadInt16() / 100f; + else if (UpdateFlagAContains(flagsA, UpdateFlagA.Z4)) + nextTransformData.Position.z = r.ReadSingle(); + else + nextTransformData.Position.z = prevTransformData.Position.z; + //If remaining has changed then a position was read. + if (readerRemaining != r.Remaining) + changedFull |= ChangedFull.Position; + + //Rotation. + if (UpdateFlagAContains(flagsA, UpdateFlagA.Rotation)) + { + //Always use _packing value even if nested. + nextTransformData.Rotation = r.ReadQuaternion(_packing.Rotation); + changedFull |= ChangedFull.Rotation; + } + else + { + nextTransformData.Rotation = prevTransformData.Rotation; + } + + //Extended settings. + if (UpdateFlagAContains(flagsA, UpdateFlagA.Extended)) + { + UpdateFlagB flagsB = (UpdateFlagB)r.ReadByte(); + readerRemaining = r.Remaining; + + //X + if (UpdateFlagBContains(flagsB, UpdateFlagB.X2)) + nextTransformData.Scale.x = r.ReadInt16() / 100f; + else if (UpdateFlagBContains(flagsB, UpdateFlagB.X4)) + nextTransformData.Scale.x = r.ReadSingle(); + else + nextTransformData.Scale.x = prevTransformData.Scale.x; + //Y + if (UpdateFlagBContains(flagsB, UpdateFlagB.Y2)) + nextTransformData.Scale.y = r.ReadInt16() / 100f; + else if (UpdateFlagBContains(flagsB, UpdateFlagB.Y4)) + nextTransformData.Scale.y = r.ReadSingle(); + else + nextTransformData.Scale.y = prevTransformData.Scale.y; + //X + if (UpdateFlagBContains(flagsB, UpdateFlagB.Z2)) + nextTransformData.Scale.z = r.ReadInt16() / 100f; + else if (UpdateFlagBContains(flagsB, UpdateFlagB.Z4)) + nextTransformData.Scale.z = r.ReadSingle(); + else + nextTransformData.Scale.z = prevTransformData.Scale.z; + + if (r.Remaining != readerRemaining) + changedFull |= ChangedFull.Scale; + else + nextTransformData.Scale = prevTransformData.Scale; + + if (UpdateFlagBContains(flagsB, UpdateFlagB.Nested)) + { + nextTransformData.ParentBehaviour = r.ReadNetworkBehaviour(); + changedFull |= ChangedFull.Nested; + } + else + { + Unnest(); + } + } + //No extended settings. + else + { + nextTransformData.Scale = prevTransformData.Scale; + Unnest(); + } + } + + void Unnest() + { + nextTransformData.ParentBehaviour = null; + } + + //Returns if whole contains part. + bool UpdateFlagAContains(UpdateFlagA whole, UpdateFlagA part) + { + return (whole & part) == part; + } + //Returns if whole contains part. + bool UpdateFlagBContains(UpdateFlagB whole, UpdateFlagB part) + { + return (whole & part) == part; + } + } + + + + /// + /// Moves to a GoalData. Automatically determins if to use data from server or client. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void MoveToTarget(float deltaOverride = -1f) + { + if (!_queueReady) + return; + //Cannot move if neither is active. + if (!base.IsServer && !base.IsClient) + return; + //If client auth and the owner don't move towards target. + if (_clientAuthoritative) + { + if (base.IsOwner || TakenOwnership) + return; + } + else + { + //If not client authoritative, is owner, and don't sync to owner. + if (base.IsOwner && !_sendToOwner) + return; + } + + //True if not client controlled. + bool controlledByClient = (_clientAuthoritative && base.Owner.IsActive); + //If not controlled by client and is server then no reason to move. + if (!controlledByClient && base.IsServer) + return; + + float delta = (deltaOverride != -1f) ? deltaOverride : Time.deltaTime; + /* Once here it's safe to assume the object will be moving. + * Any checks which would stop it from moving be it client + * auth and owner, or server controlled and server, ect, + * would have already been run. */ + TransformData td = _currentGoalData.Transforms; + RateData rd = _currentGoalData.Rates; + + + + float multiplier = 1f; + int queueCount = _goalDataQueue.Count; + //For every entry past interpolation increase move rate. + if (queueCount > (_interpolation + 1)) + multiplier += (0.05f * queueCount); + + //Rate to update. Changes per property. + float rate; + Transform t = transform; + + //Snap any positions that should be. + SnapProperties(td); + + //Position. + if (_synchronizePosition) + { + rate = rd.Position; + Vector3 posGoal = (td.ExtrapolationState == TransformData.ExtrapolateState.Active && !_lastReceiveReliable) ? td.ExtrapolatedPosition : td.Position; + if (rate == -1f) + t.localPosition = td.Position; + else + t.localPosition = Vector3.MoveTowards(t.localPosition, posGoal, rate * delta * multiplier); + } + + //Rotation. + if (_synchronizeRotation) + { + rate = rd.Rotation; + if (rate == -1f) + t.localRotation = td.Rotation; + else + t.localRotation = Quaternion.RotateTowards(t.localRotation, td.Rotation, rate * delta); + } + + //Scale. + if (_synchronizeScale) + { + rate = rd.Scale; + if (rate == -1f) + t.localScale = td.Scale; + else + t.localScale = Vector3.MoveTowards(t.localScale, td.Scale, rate * delta); + } + + float timeRemaining = rd.TimeRemaining - (delta * multiplier); + if (timeRemaining < -delta) + timeRemaining = -delta; + rd.TimeRemaining = timeRemaining; + + if (rd.TimeRemaining <= 0f) + { + float leftOver = Mathf.Abs(rd.TimeRemaining); + //If more in buffer then run next buffer. + if (queueCount > 0) + { + _currentGoalData.Reset(); + _goalDataCache.Push(_currentGoalData); + SetCurrentGoalData(_goalDataQueue.Dequeue()); + if (leftOver > 0f) + MoveToTarget(leftOver); + } + //No more in buffer, see if can extrapolate. + else + { + + /* If everything matches up then end queue. + * Otherwise let it play out until stuff + * aligns. Generally the time remaining is enough + * but every once in awhile something goes funky + * and it's thrown off. */ + if (!HasChanged(td)) + _queueReady = false; + OnInterpolationComplete?.Invoke(); + + } + } + + } + + /// + /// Sends transform data to clients if needed. + /// + private void SendToClients(byte lodIndex) + { + //True if clientAuthoritative and there is an owner. + bool clientAuthoritativeWithOwner = (_clientAuthoritative && base.Owner.IsValid && !base.Owner.IsLocalClient); + //Channel to send rpc on. + Channel channel = Channel.Unreliable; + //If relaying from client. + if (clientAuthoritativeWithOwner) + { + if (_receivedClientData.HasData[lodIndex]) + { + _changedSinceStart = true; + //Resend data from clients. + ObserversUpdateTransform(_receivedClientData.Writer.GetArraySegment(), _receivedClientData.Channel); + _receivedClientData.SetHasData(false, lodIndex); + } + } + //Sending server transform state. + else + { + //Becomes true when any lod changes. + bool dataChanged = false; + //Check changes for every lod at and below passed in index. + for (int i = lodIndex; i >= 0; i--) + { + /* Reset writer. If does not have value + * after these checks then we know + * there's nothing to send for this lod. */ + PooledWriter writer = _changedWriters[i]; + writer.Reset(); + + TransformData lastSentData = _lastSentTransformDatas[i]; + ChangedDelta changed = GetChanged(lastSentData); + //If no change. + if (changed == ChangedDelta.Unset) + { + //No changes since last reliable; transform is up to date. + if (_serverChangedSinceReliable[i] == ChangedDelta.Unset) + continue; + + //Set changed to all changes over time and unset changes over time. + changed = _serverChangedSinceReliable[lodIndex]; + _serverChangedSinceReliable[i] = ChangedDelta.Unset; + channel = Channel.Reliable; + } + //There is change. + else + { + _serverChangedSinceReliable[i] |= changed; + } + + dataChanged = true; + _changedSinceStart = true; + Transform t = transform; + /* If here a send for transform values will occur. Update last values. + * Tick doesn't need to be set for whoever controls transform. */ + lastSentData.Update(0, t.localPosition, t.localRotation, t.localScale, t.localPosition, _parentBehaviour); + + SerializeChanged(changed, writer); + } + + //Send out changes. + if (dataChanged) + { + foreach (NetworkConnection nc in base.Observers) + { + //If to not send to owner. + if (!_sendToOwner && nc == base.Owner) + continue; + + byte lod; + if (!nc.LevelOfDetails.TryGetValue(base.NetworkObject, out lod)) + lod = 0; + //Not high enough index to send to conn. + if (lod > lodIndex) + continue; + //No need for server to send to local client (clientHost). + //Still send if development for stat tracking. +#if !DEVELOPMENT + if (!nc.IsLocalClient) +#endif + TargetUpdateTransform(nc, _changedWriters[lodIndex].GetArraySegment(), channel); + } + } + } + + } + + /// + /// Sends transform data to server if needed. + /// + private void SendToServer(TransformData lastSentTransformData) + { + /* ClientHost does not need to send to the server. + * Ideally this would still occur and the data be ignored + * for statistics tracking but to keep the code more simple + * we won't be doing that. Server out however still is tracked, + * which is generally considered more important data. */ + if (base.IsServer) + return; + + //Not client auth or not owner. + if (!_clientAuthoritative || !base.IsOwner) + return; + + //Channel to send on. + Channel channel = Channel.Unreliable; + //Values changed since last check. + ChangedDelta changed = GetChanged(lastSentTransformData); + + //If no change. + if (changed == ChangedDelta.Unset) + { + //No changes since last reliable; transform is up to date. + if (_clientChangedSinceReliable == ChangedDelta.Unset) + return; + + //Set changed to all changes over time and unset changes over time. + changed = _clientChangedSinceReliable; + _clientChangedSinceReliable = ChangedDelta.Unset; + channel = Channel.Reliable; + } + //There is change. + else + { + _clientChangedSinceReliable |= changed; + } + + /* If here a send for transform values will occur. Update last values. + * Tick doesn't need to be set for whoever controls transform. */ + Transform t = transform; + lastSentTransformData.Update(0, t.localPosition, t.localRotation, t.localScale, t.localPosition, _parentBehaviour); + + //Send latest. + using (PooledWriter writer = WriterPool.GetWriter()) + { + SerializeChanged(changed, writer); + ServerUpdateTransform(writer.GetArraySegment(), channel); + } + } + + + #region GetChanged. + /// + /// Returns if the transform differs from td. + /// + private bool HasChanged(TransformData td) + { + bool changed = (td.Position != transform.localPosition || + td.Rotation != transform.localRotation || + td.Scale != transform.localScale); + + return changed; + } + /// + /// Returns if there is any change between two datas. + /// + private bool HasChanged(TransformData a, TransformData b) + { + return (a.Position != b.Position) || + (a.Rotation != b.Rotation) || + (a.Scale != b.Scale) || + (a.ParentBehaviour != b.ParentBehaviour); + } + /// + /// Returns if there is any change between two datas and outputs what has changed. + /// + private bool HasChanged(TransformData a, TransformData b, ref ChangedFull changedFull) + { + bool hasChanged = false; + + if (a.Position != b.Position) + { + hasChanged = true; + changedFull |= ChangedFull.Position; + } + if (a.Rotation != b.Rotation) + { + hasChanged = true; + changedFull |= ChangedFull.Rotation; + } + if (a.Scale != b.Scale) + { + hasChanged = true; + changedFull |= ChangedFull.Scale; + } + if (a.ParentBehaviour != b.ParentBehaviour) + { + hasChanged = true; + changedFull |= ChangedFull.Nested; + } + + return hasChanged; + } + /// + /// Gets transform values that have changed against goalData. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ChangedDelta GetChanged(TransformData transformData) + { + /* If parent behaviour exist. + * Parent isn't sent as a delta so + * if it exist always send regardless + * of the previously sent transform + * data. */ + return GetChanged(ref transformData.Position, ref transformData.Rotation, ref transformData.Scale, transformData.ParentBehaviour); + } + /// + /// Gets transform values that have changed against specified proprties. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ChangedDelta GetChanged(ref Vector3 lastPosition, ref Quaternion lastRotation, ref Vector3 lastScale, NetworkBehaviour lastParentBehaviour) + { + ChangedDelta changed = ChangedDelta.Unset; + Transform t = transform; + + Vector3 position = t.localPosition; + if (position.x != lastPosition.x) + changed |= ChangedDelta.PositionX; + if (position.y != lastPosition.y) + changed |= ChangedDelta.PositionY; + if (position.z != lastPosition.z) + changed |= ChangedDelta.PositionZ; + + Quaternion rotation = t.localRotation; + if (!rotation.Matches(lastRotation, true)) + changed |= ChangedDelta.Rotation; + + ChangedDelta startChanged; + startChanged = changed; + + Vector3 scale = t.localScale; + if (scale.x != lastScale.x) + changed |= ChangedDelta.ScaleX; + if (scale.y != lastScale.y) + changed |= ChangedDelta.ScaleY; + if (scale.z != lastScale.z) + changed |= ChangedDelta.ScaleZ; + + //Only include parent if there is additional data to send. + bool sendParent = (_parentBehaviour == null && lastParentBehaviour != null) || (changed != ChangedDelta.Unset && _parentBehaviour != null); + if (sendParent) + changed |= ChangedDelta.Nested; + + //If added scale or nested then also add extended. + if (startChanged != changed) + changed |= ChangedDelta.Extended; + + return changed; + } + #endregion + + #region Rates. + /// + /// Snaps transform properties using snapping settings. + /// + private void SnapProperties(TransformData transformData, bool force = false) + { + //Already snapped. + if (transformData.Snapped) + return; + + transformData.Snapped = true; + Transform t = transform; + + //Position. + if (_synchronizePosition) + { + Vector3 position; + position.x = (force || _positionSnapping.X) ? transformData.Position.x : t.localPosition.x; + position.y = (force || _positionSnapping.Y) ? transformData.Position.y : t.localPosition.y; + position.z = (force || _positionSnapping.Z) ? transformData.Position.z : t.localPosition.z; + t.localPosition = position; + } + + //Rotation. + if (_synchronizeRotation) + { + Vector3 eulers; + Vector3 goalEulers = transformData.Rotation.eulerAngles; + eulers.x = (force || _rotationSnapping.X) ? goalEulers.x : t.localEulerAngles.x; + eulers.y = (force || _rotationSnapping.Y) ? goalEulers.y : t.localEulerAngles.y; + eulers.z = (force || _rotationSnapping.Z) ? goalEulers.z : t.localEulerAngles.z; + t.localEulerAngles = eulers; + } + + //Scale. + if (_synchronizeScale) + { + Vector3 scale; + scale.x = (force || _scaleSnapping.X) ? transformData.Scale.x : t.localScale.x; + scale.y = (force || _scaleSnapping.Y) ? transformData.Scale.y : t.localScale.y; + scale.z = (force || _scaleSnapping.Z) ? transformData.Scale.z : t.localScale.z; + t.localScale = scale; + } + } + + /// + /// Sets move rates which will occur instantly. + /// + private void SetInstantRates(RateData rd) + { + rd.Update(-1f, -1f, -1f, -1f, 1, false, -1f); + } + + /// + /// Sets move rates which will occur over time. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SetCalculatedRates(uint lastTick, RateData prevRd, TransformData prevTd, GoalData nextGd, ChangedFull changedFull, bool hasChanged, Channel channel) + { + /* Only update rates if data has changed. + * When data comes in reliably for eventual consistency + * it's possible that it will be the same as the last + * unreliable packet. When this happens no change has occurred + * and the distance of change woudl also be 0; this prevents + * the NT from moving. Only need to compare data if channel is reliable. */ + TransformData td = nextGd.Transforms; + if (channel == Channel.Reliable && !hasChanged) + { + nextGd.Rates.Update(prevRd); + return; + } + + /* How much time has passed between last update and current. + * If set to 0 then that means the transform has + * settled. */ + if (lastTick == 0) + lastTick = (nextGd.Transforms.Tick - _interval); + + uint tickDifference = (td.Tick - lastTick); + float timePassed = (float)base.NetworkManager.TimeManager.TicksToTime(tickDifference); + + //Distance between properties. + float distance; + float positionRate = 0f; + float rotationRate = 0f; + float scaleRate = 0f; + + RateData rd = nextGd.Rates; + //Correction to apply towards rates when a rate change is detected as abnormal. + float abnormalCorrection = 1f; + bool abnormalRateDetected = false; + float unalteredPositionRate = rd.LastUnalteredPositionRate; + + //Position. + if (ChangedFullContains(changedFull, ChangedFull.Position)) + { + Vector3 lastPosition = prevTd.Position; + distance = Vector3.Distance(lastPosition, td.Position); + //If distance teleports assume rest do. + if (_enableTeleport && distance >= _teleportThreshold) + { + SetInstantRates(rd); + return; + } + + //Position distance already calculated. + unalteredPositionRate = distance / timePassed; + /* Try to detect abnormal rate changes. + * + * This won't occur if the user + * is moving using the tick system but will likely happen when the transform + * is being moved in update. + * + * Update will iterate a varying amount of times per tick, + * which will result in distances being slightly different. This is + * rarely an issue when the frame rate is high and the distance + * variance is very little, but for games which are running at about + * the same frame rate as the tick it's possible the object will + * move twice the distance every few ticks. EG: if running 60 fps/50 tick. + * Execution may look like this.. + * frame, tick, frame, tick, frame, frame, tick. The frame, frame would + * result in double movement distance. */ + + //If last position rate is known then compare against it. + if (unalteredPositionRate > 0f && rd.LastUnalteredPositionRate > 0f) + { + float percentage = Mathf.Abs(1f - (unalteredPositionRate / rd.LastUnalteredPositionRate)); + /* If percentage change is more than 25% then speed is considered + * to have changed drastically. */ + if (percentage > 0.25f) + { + float c = (rd.LastUnalteredPositionRate / unalteredPositionRate); + /* Sometimes stop and goes can incorrectly trigger + * an abnormal detection. Fortunately abnornalties tend + * to either skip a tick or send twice in one tick. + * Because of this it's fairly safe to assume that if the calculated + * correction is not ~0.5f or ~2f then it's a false detection. */ + float allowedDifference = 0.1f; + if ( + (c < 1f && Mathf.Abs(0.5f - c) < allowedDifference) || + (c > 1f && Mathf.Abs(2f - c) < allowedDifference)) + { + abnormalCorrection = c; + abnormalRateDetected = true; + } + /* If an abnormality has been marked then assume new rate + * is proper. When an abnormal rate occurs unintentionally + * the values will fix themselves next tick, therefor when + * rate changes drastically twice assume its intentional or + * that the rate had simply fixed itself, both which would unset + * abnormal rate detected. */ + } + } + + //abnormalCorrection = 1f; + positionRate = (unalteredPositionRate * abnormalCorrection); + if (positionRate <= 0f) + positionRate = -1f; + } + + //Rotation. + if (ChangedFullContains(changedFull, ChangedFull.Rotation)) + { + Quaternion lastRotation = prevTd.Rotation; + distance = lastRotation.Angle(td.Rotation, true); + rotationRate = (distance / timePassed) * abnormalCorrection; + if (rotationRate <= 0f) + rotationRate = -1f; + } + + //Scale. + if (ChangedFullContains(changedFull, ChangedFull.Scale)) + { + Vector3 lastScale = prevTd.Scale; + distance = Vector3.Distance(lastScale, td.Scale); + scaleRate = (distance / timePassed) * abnormalCorrection; + if (scaleRate <= 0f) + scaleRate = -1f; + } + + rd.Update(positionRate, rotationRate, scaleRate, unalteredPositionRate, tickDifference, abnormalRateDetected, timePassed); + + //Returns if whole contains part. + bool ChangedFullContains(ChangedFull whole, ChangedFull part) + { + return (whole & part) == part; + } + } + #endregion + + /// + /// Sets extrapolation data on next. + /// + /// + /// + /// + private void SetExtrapolation(TransformData prev, TransformData next, Channel channel) + { + //Default value. + next.ExtrapolationState = TransformData.ExtrapolateState.Disabled; + + + } + + + /// + /// Updates a client with transform data. + /// + [TargetRpc(ValidateTarget = false)] + private void TargetUpdateTransform(NetworkConnection conn, ArraySegment data, Channel channel) + { +#if DEVELOPMENT + //If receiver is client host then do nothing, clientHost need not process. + if (base.IsServer && conn.IsLocalClient) + return; +#endif + + byte lod; + /* Get cached LOD for connection receiving this. It will of course + * always be the local client for conn. */ + if (!conn.LevelOfDetails.TryGetValue(base.NetworkObject, out lod)) + lod = 0; + + //Update the interval so speed calculations are proper. + _interval = base.ObserverManager.GetLevelOfDetailInterval(lod); + DataReceived(data, channel, false); + } + + /// + /// Updates clients with transform data. + /// + [ObserversRpc] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ObserversUpdateTransform(ArraySegment data, Channel channel) + { + if (!_clientAuthoritative && base.IsOwner && !_sendToOwner) + return; + if (_clientAuthoritative && base.IsOwner) + return; + if (base.IsServer) + return; + + //Not new data. + uint lastPacketTick = base.TimeManager.LastPacketTick; + if (lastPacketTick <= _lastObserversRpcTick) + return; + _lastObserversRpcTick = lastPacketTick; + + DataReceived(data, channel, false); + } + + ///// + ///// Updates clients with transform data. + ///// + //[TargetRpc(ValidateTarget = false)] + //[MethodImpl(MethodImplOptions.AggressiveInlining)] + //private void TargetUpdateTransform(NetworkConnection conn, ArraySegment data, Channel channel) + //{ + // if (!_clientAuthoritative && base.IsOwner && !_sendToOwner) + // return; + // if (_clientAuthoritative && base.IsOwner) + // return; + // if (base.IsServer) + // return; + + // //Not new data. + // uint lastPacketTick = base.TimeManager.LastPacketTick; + // if (lastPacketTick <= _lastObserversRpcTick) + // return; + // _lastObserversRpcTick = lastPacketTick; + + // DataReceived(data, channel, false); + //} + + /// + /// Updates the transform on the server. + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ServerRpc] + private void ServerUpdateTransform(ArraySegment data, Channel channel) + { + if (!_clientAuthoritative) + { + base.Owner.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"Connection Id {base.Owner.ClientId} has been kicked for trying to update this object without client authority."); + return; + } + + //Not new data. + uint lastPacketTick = base.TimeManager.LastPacketTick; + if (lastPacketTick <= _lastServerRpcTick) + return; + _lastServerRpcTick = lastPacketTick; + + //Populate writer if it doesn't exist. + if (_receivedClientData.Writer == null) + _receivedClientData.Writer = WriterPool.GetWriter(); + _receivedClientData.Channel = channel; + _receivedClientData.Writer.Reset(); + _receivedClientData.Writer.WriteArraySegment(data); + _receivedClientData.SetHasData(true); + + DataReceived(data, channel, true); + } + + /// + /// Processes received data for lcients and server. + /// + private void DataReceived(ArraySegment data, Channel channel, bool asServer) + { + TransformData prevTd = _lastReceivedTransformData; + RateData prevRd = _lastCalculatedRateData; + ChangedFull changedFull = new ChangedFull(); + + GoalData nextGd = GetCachedGoalData(); + TransformData nextTd = nextGd.Transforms; + UpdateTransformData(data, prevTd, nextTd, ref changedFull); + OnDataReceived?.Invoke(prevTd, nextTd); + SetExtrapolation(prevTd, nextTd, channel); + + bool hasChanged = HasChanged(prevTd, nextTd); + //If server only teleport. + if (asServer && !base.IsClient) + SetInstantRates(nextGd.Rates); + //Otherwise use timed. + else + SetCalculatedRates(prevTd.Tick, prevRd, prevTd, nextGd, changedFull, hasChanged, channel); + + _lastReceivedTransformData.Update(nextTd); + + _lastReceiveReliable = (channel == Channel.Reliable); + /* If channel is reliable then this is a settled packet. + * Reset last received tick so next starting move eases + * in. */ + if (channel == Channel.Reliable) + nextTd.Tick = 0; + + prevTd.Update(nextTd); + prevRd.Update(nextGd.Rates); + + nextGd.ReceivedTick = base.TimeManager.LocalTick; + + /* If extrapolating then immediately break the extrapolation + * in favor of newest results. This will keep the buffer + * at 0 until the transform settles but the only other option is + * to stop the movement, which would defeat purpose of extrapolation, + * or slow down the transform while buffer rebuilds. Neither choice + * is great but later on I might try slowing down the transform slightly + * to give the buffer a chance to rebuild. */ + if (_currentGoalData.Transforms.ExtrapolationState == TransformData.ExtrapolateState.Active) + { + _queueReady = true; + SetCurrentGoalData(nextGd); + } + /* If queue isn't started and its buffered enough + * to satisfy interpolation then set ready + * and set current data. + * + * Also if reliable then begin moving. */ + else if (!_queueReady && _goalDataQueue.Count >= _interpolation + || channel == Channel.Reliable) + { + _queueReady = true; + if (_goalDataQueue.Count > 0) + { + SetCurrentGoalData(_goalDataQueue.Dequeue()); + /* If is reliable and has changed then also + * enqueue latest. */ + if (hasChanged) + _goalDataQueue.Enqueue(nextGd); + + } + else + { + SetCurrentGoalData(nextGd); + } + } + /* If here then there's not enough in buffer to begin + * so add onto the buffer. */ + else + { + _goalDataQueue.Enqueue(nextGd); + } + + /* If the queue is excessive beyond interpolation then + * dequeue extras to prevent from dropping behind too + * quickly. This shouldn't be an issue with normal movement + * as the NT speeds up if the buffer unexpectedly grows, but + * when connections are unstable results may come in chunks + * and for a better experience the older parts of the chunks + * will be dropped. */ + if (_goalDataQueue.Count > (_interpolation + 3)) + { + while (_goalDataQueue.Count > _interpolation) + { + GoalData tmpGd = _goalDataQueue.Dequeue(); + _goalDataCache.Push(tmpGd); + } + //Snap to the next data to fix any smoothing timings. + SetCurrentGoalData(_goalDataQueue.Dequeue()); + SetInstantRates(_currentGoalData.Rates); + SnapProperties(_currentGoalData.Transforms, true); + } + } + + /// + /// Sets CurrentGoalData value. + /// + /// + private void SetCurrentGoalData(GoalData data) + { + _currentGoalData = data; + OnNextGoal?.Invoke(data); + } + + /// + /// Immediately sets the parent of this NetworkTransform for a single connection. + /// + [TargetRpc] + private void TargetSetParent(NetworkConnection conn, NetworkBehaviour parent) + { + + } + + /// + /// Updates a TransformData from packetData. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void UpdateTransformData(ArraySegment packetData, TransformData prevTransformData, TransformData nextTransformData, ref ChangedFull changedFull) + { + DeserializePacket(packetData, prevTransformData, nextTransformData, ref changedFull); + nextTransformData.Tick = base.TimeManager.LastPacketTick; + } + + /// + /// Returns a GoalData from the cache. + /// + /// + private GoalData GetCachedGoalData() + { + GoalData result = (_goalDataCache.Count > 0) ? _goalDataCache.Pop() : new GoalData(); + result.Reset(); + return result; + } + + /// + /// Configures this NetworkTransform for CSP. + /// + internal void ConfigureForCSP() + { + _clientAuthoritative = false; + if (base.IsServer) + _sendToOwner = false; + + /* If other or CC then needs to be configured. + * When CC it will be configured properly, if there + * is no CC then no action will be taken. */ + _componentConfiguration = ComponentConfigurationType.CharacterController; + ConfigureComponents(); + } + + /// + /// Updates which properties are synchronized. + /// + /// Properties to synchronize. + public void SetSynchronizedProperties(SynchronizedProperty value) + { + /* Make sure permissions are proper to change values. + * Let the server override client auth. + * + * Can send if server. + * Or owner + client auth. + */ + bool canSend = ( + base.IsServer || + (_clientAuthoritative && base.IsOwner) + ); + + if (!canSend) + return; + + //If server send out observerRpc. + if (base.IsServer) + ObserversSetSynchronizedProperties(value); + //Otherwise send to the server. + else + ServerSetSynchronizedProperties(value); + } + + /// + /// Sets synchronized values based on value. + /// + [ServerRpc] + private void ServerSetSynchronizedProperties(SynchronizedProperty value) + { + if (!_clientAuthoritative) + { + base.Owner.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"Connection Id {base.Owner.ClientId} has been kicked for trying to update this object without client authority."); + return; + } + + SetSynchronizedPropertiesInternal(value); + ObserversSetSynchronizedProperties(value); + } + + /// + /// Sets synchronized values based on value. + /// + [ObserversRpc(BufferLast = true)] + private void ObserversSetSynchronizedProperties(SynchronizedProperty value) + { + //Would have already run on server if host. + if (base.IsServer) + return; + + SetSynchronizedPropertiesInternal(value); + } + + /// + /// Sets synchronized values based on value. + /// + private void SetSynchronizedPropertiesInternal(SynchronizedProperty value) + { + _synchronizeParent = SynchronizedPropertyContains(value, SynchronizedProperty.Parent); + _synchronizePosition = SynchronizedPropertyContains(value, SynchronizedProperty.Position); + _synchronizeRotation = SynchronizedPropertyContains(value, SynchronizedProperty.Rotation); + _synchronizeScale = SynchronizedPropertyContains(value, SynchronizedProperty.Scale); + + bool SynchronizedPropertyContains(SynchronizedProperty whole, SynchronizedProperty part) + { + return (whole & part) == part; + } + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/NetworkTransform.cs.meta b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/NetworkTransform.cs.meta new file mode 100644 index 0000000..76f2fc8 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/NetworkTransform.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a2836e36774ca1c4bbbee976e17b649c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/SynchronizedProperty.cs b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/SynchronizedProperty.cs new file mode 100644 index 0000000..a154521 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/SynchronizedProperty.cs @@ -0,0 +1,13 @@ +namespace FishNet.Component.Transforming +{ + + public enum SynchronizedProperty : byte + { + None = 0, + Parent = 1, + Position = 2, + Rotation = 4, + Scale = 8 + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/SynchronizedProperty.cs.meta b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/SynchronizedProperty.cs.meta new file mode 100644 index 0000000..28fe681 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/SynchronizedProperty.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3e6005ee9abfdd542ad27023114bbe04 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction.meta b/Assets/FishNet/Runtime/Generated/Component/Prediction.meta new file mode 100644 index 0000000..aabf13e --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e4f8fbf54adbf5d4781d2c88c0aaebd8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/Editor.meta b/Assets/FishNet/Runtime/Generated/Component/Prediction/Editor.meta new file mode 100644 index 0000000..9e480d2 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a6b05a47941365c4097d74bca5e47017 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/Editor/PredictedObjectEditor.cs b/Assets/FishNet/Runtime/Generated/Component/Prediction/Editor/PredictedObjectEditor.cs new file mode 100644 index 0000000..a75df56 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/Editor/PredictedObjectEditor.cs @@ -0,0 +1,152 @@ +#if UNITY_EDITOR +using UnityEditor; +using UnityEngine; +using static FishNet.Component.Prediction.PredictedObject; + +namespace FishNet.Component.Prediction +{ + + + [CustomEditor(typeof(PredictedObject), true)] + [CanEditMultipleObjects] + public class PredictedObjectEditor : Editor + { + private SerializedProperty _implementsPredictionMethods; + private SerializedProperty _graphicalObject; + private SerializedProperty _ownerSmoothPosition; + private SerializedProperty _ownerSmoothRotation; + private SerializedProperty _ownerInterpolation; + private SerializedProperty _enableTeleport; + private SerializedProperty _teleportThreshold; + private SerializedProperty _predictionType; + + private SerializedProperty _rigidbody; + private SerializedProperty _rigidbody2d; + private SerializedProperty _spectatorSmoothPosition; + private SerializedProperty _spectatorSmoothRotation; + private SerializedProperty _spectatorSmoothingType; + private SerializedProperty _customSmoothingData; + private SerializedProperty _preconfiguredSmoothingDataPreview; + private SerializedProperty _maintainedVelocity; + private SerializedProperty _resendType; + private SerializedProperty _resendInterval; + + private SerializedProperty _networkTransform; + + protected virtual void OnEnable() + { + _implementsPredictionMethods = serializedObject.FindProperty(nameof(_implementsPredictionMethods)); + _graphicalObject = serializedObject.FindProperty(nameof(_graphicalObject)); + _ownerSmoothPosition = serializedObject.FindProperty(nameof(_ownerSmoothPosition)); + _ownerSmoothRotation = serializedObject.FindProperty(nameof(_ownerSmoothRotation)); + _ownerInterpolation = serializedObject.FindProperty(nameof(_ownerInterpolation)); + _enableTeleport = serializedObject.FindProperty(nameof(_enableTeleport)); + _teleportThreshold = serializedObject.FindProperty(nameof(_teleportThreshold)); + _predictionType = serializedObject.FindProperty(nameof(_predictionType)); + + _rigidbody = serializedObject.FindProperty(nameof(_rigidbody)); + _rigidbody2d = serializedObject.FindProperty(nameof(_rigidbody2d)); + _spectatorSmoothPosition = serializedObject.FindProperty(nameof(_spectatorSmoothPosition)); + _spectatorSmoothRotation = serializedObject.FindProperty(nameof(_spectatorSmoothRotation)); + _spectatorSmoothingType = serializedObject.FindProperty(nameof(_spectatorSmoothingType)); + _customSmoothingData = serializedObject.FindProperty(nameof(_customSmoothingData)); + _preconfiguredSmoothingDataPreview = serializedObject.FindProperty(nameof(_preconfiguredSmoothingDataPreview)); + + _maintainedVelocity = serializedObject.FindProperty(nameof(_maintainedVelocity)); + _resendType = serializedObject.FindProperty(nameof(_resendType)); + _resendInterval = serializedObject.FindProperty(nameof(_resendInterval)); + + _networkTransform = serializedObject.FindProperty(nameof(_networkTransform)); + + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + GUI.enabled = false; + EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour((PredictedObject)target), typeof(PredictedObject), false); + GUI.enabled = true; + + EditorGUILayout.PropertyField(_implementsPredictionMethods); + EditorGUILayout.PropertyField(_graphicalObject); + EditorGUILayout.PropertyField(_enableTeleport); + if (_enableTeleport.boolValue) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_teleportThreshold); + EditorGUI.indentLevel--; + } + + EditorGUILayout.LabelField("Owner Settings"); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_ownerSmoothPosition, new GUIContent("Smooth Position")); + EditorGUILayout.PropertyField(_ownerSmoothRotation, new GUIContent("Smooth Rotation")); + EditorGUILayout.PropertyField(_ownerInterpolation, new GUIContent("Interpolation")); + EditorGUI.indentLevel--; + + EditorGUILayout.PropertyField(_predictionType); + PredictedObject.PredictionType movementType = (PredictedObject.PredictionType)_predictionType.intValue; + if (movementType != PredictedObject.PredictionType.Other) + { + EditorGUI.indentLevel++; + EditorGUILayout.HelpBox("When using physics prediction do not include a NetworkTransform; this component will synchronize instead.", MessageType.Info); + if (movementType == PredictedObject.PredictionType.Rigidbody) + EditorGUILayout.PropertyField(_rigidbody); + else + EditorGUILayout.PropertyField(_rigidbody2d, new GUIContent("Rigidbody2D", "Rigidbody2D to predict.")); + + EditorGUILayout.LabelField("Spectator Settings"); + EditorGUI.indentLevel++; + EditorGUILayout.LabelField("Smoothing"); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_spectatorSmoothPosition, new GUIContent("Smooth Position")); + EditorGUILayout.PropertyField(_spectatorSmoothRotation, new GUIContent("Smooth Rotation")); + EditorGUILayout.PropertyField(_spectatorSmoothingType, new GUIContent("Smoothing Type")); + //Custom. + if ((SpectatorSmoothingType)_spectatorSmoothingType.intValue == SpectatorSmoothingType.Custom) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_customSmoothingData); + EditorGUI.indentLevel--; + } + //Preconfigured. + else + { + EditorGUI.indentLevel++; + GUI.enabled = false; + EditorGUILayout.PropertyField(_preconfiguredSmoothingDataPreview); + GUI.enabled = true; + EditorGUI.indentLevel--; + } + EditorGUI.indentLevel--; + EditorGUILayout.PropertyField(_maintainedVelocity); + + EditorGUILayout.PropertyField(_resendType); + PredictedObject.ResendType resendType = (PredictedObject.ResendType)_resendType.intValue; + if (resendType == PredictedObject.ResendType.Interval) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_resendInterval, new GUIContent("Interval")); + EditorGUI.indentLevel--; + } + EditorGUI.indentLevel--; + + EditorGUI.indentLevel--; + } + else + { + EditorGUI.indentLevel++; + EditorGUILayout.HelpBox("When other is selected another component, such as NetworkTransform, must be used to synchronize.", MessageType.Info); + EditorGUILayout.PropertyField(_networkTransform); + EditorGUI.indentLevel--; + } + + EditorGUILayout.Space(); + serializedObject.ApplyModifiedProperties(); + } + + } +} +#endif + diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/Editor/PredictedObjectEditor.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Prediction/Editor/PredictedObjectEditor.cs.meta new file mode 100644 index 0000000..2a45c05 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/Editor/PredictedObjectEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e0b8595657415764e9d83b6d974043af +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/OfflineRigidbody.cs b/Assets/FishNet/Runtime/Generated/Component/Prediction/OfflineRigidbody.cs new file mode 100644 index 0000000..b691a76 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/OfflineRigidbody.cs @@ -0,0 +1,130 @@ +using FishNet.Managing.Predicting; +using FishNet.Managing.Timing; +using FishNet.Object; +using UnityEngine; + +namespace FishNet.Component.Prediction +{ + public partial class OfflineRigidbody : MonoBehaviour + { + + #region Serialized. + /// + /// Type of prediction movement which is being used. + /// + [Tooltip("Type of prediction movement which is being used.")] + [SerializeField] + private RigidbodyType _rigidbodyType; + /// + /// GraphicalObject to unparent when pausing. + /// + private Transform _graphicalObject; + /// + /// Sets GraphicalObject. + /// + /// + public void SetGraphicalObject(Transform value) + { + _graphicalObject = value; + UpdateRigidbodies(); + } + /// + /// True to also get rigidbody components within children. + /// + [Tooltip("True to also get rigidbody components within children.")] + [SerializeField] + private bool _getInChildren; + #endregion + + #region Private. + /// + /// Pauser for rigidbodies. + /// + private RigidbodyPauser _rigidbodyPauser = new RigidbodyPauser(); + /// + /// TimeManager subscribed to. + /// + private PredictionManager _predictionManager; + #endregion + + + private void Awake() + { + InitializeOnce(); + } + + + private void OnDestroy() + { + ChangeSubscription(false); + } + + /// + /// Initializes this script for use. + /// + private void InitializeOnce() + { + _predictionManager = InstanceFinder.PredictionManager; + UpdateRigidbodies(); + ChangeSubscription(true); + } + + /// + /// Sets a new TimeManager to use. + /// + /// + public void SetPredictionManager(PredictionManager pm) + { + if (pm == _predictionManager) + return; + + //Unsub from current. + ChangeSubscription(false); + //Sub to newest. + _predictionManager = pm; + ChangeSubscription(true); + } + + /// + /// Finds and assigns rigidbodie using configured settings. + /// + public void UpdateRigidbodies() + { + _rigidbodyPauser.UpdateRigidbodies(transform, _rigidbodyType, _getInChildren, _graphicalObject); + } + + /// + /// Changes the subscription to the TimeManager. + /// + private void ChangeSubscription(bool subscribe) + { + if (_predictionManager == null) + return; + + if (subscribe) + { + _predictionManager.OnPreReconcile += _predictionManager_OnPreReconcile; + _predictionManager.OnPostReconcile += _predictionManager_OnPostReconcile; + } + else + { + _predictionManager.OnPreReconcile -= _predictionManager_OnPreReconcile; + _predictionManager.OnPostReconcile -= _predictionManager_OnPostReconcile; + } + } + + private void _predictionManager_OnPreReconcile(NetworkBehaviour obj) + { + //Make rbs all kinematic/!simulated before reconciling, which would also result in replays. + _rigidbodyPauser.Pause(); + } + + private void _predictionManager_OnPostReconcile(NetworkBehaviour obj) + { + _rigidbodyPauser.Unpause(); + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/OfflineRigidbody.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Prediction/OfflineRigidbody.cs.meta new file mode 100644 index 0000000..cd25d95 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/OfflineRigidbody.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b749f90d4c9961c4991179db1130fa4d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObject.Rigidbodies.cs b/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObject.Rigidbodies.cs new file mode 100644 index 0000000..ceb2985 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObject.Rigidbodies.cs @@ -0,0 +1,1148 @@ +using FishNet.Connection; +using FishNet.Managing; +using FishNet.Managing.Timing; +using FishNet.Object; +using FishNet.Transporting; +using FishNet.Utility; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Component.Prediction +{ + public partial class PredictedObject : NetworkBehaviour + { + #region Types. + [System.Serializable] + public struct SmoothingData + { + /// + /// Percentage of ping to use as interpolation. Higher values will result in more interpolation. + /// + [Tooltip("Percentage of ping to use as interpolation. Higher values will result in more interpolation.")] + [Range(0.01f, 5f)] + public float InterpolationPercent; + /// + /// Percentage of ping to use as interpolation when colliding with an object local client owns. + /// This is used to speed up local interpolation when predicted objects collide with a player as well keep graphics closer to the objects root while colliding. + /// + [Tooltip("Percentage of ping to use as interpolation when colliding with an object local client owns." + + "This is used to speed up local interpolation when predicted objects collide with a player as well keep graphics closer to the objects root while colliding.")] + [Range(0.01f, 5f)] + public float CollisionInterpolationPercent; + /// + /// How much per tick to decrease to collision interpolation when colliding with a local player object. + /// Higher values will set interpolation to collision settings faster. + /// + [Tooltip("How much per tick to decrease to collision interpolation when colliding with a local player object. Higher values will set interpolation to collision settings faster.")] + [Range(1, byte.MaxValue)] + public byte InterpolationDecreaseStep; + /// + /// How much per tick to increase to normal interpolation when not colliding with a local player object. + /// Higher values will set interpolation to normal settings faster. + /// + [Tooltip("How much per tick to increase to normal interpolation when not colliding with a local player object. Higher values will set interpolation to normal settings faster.")] + [Range(1, byte.MaxValue)] + public byte InterpolationIncreaseStep; + } + #endregion + + #region All. + #region Internal. + /// + /// True if owner and implements prediction methods. + /// + internal bool IsPredictingOwner() => (base.IsOwner && _implementsPredictionMethods); + #endregion + #region Private. + /// + /// Pauser for rigidbodies when they cannot be rolled back. + /// + private RigidbodyPauser _rigidbodyPauser = new RigidbodyPauser(); + /// + /// Next tick to resend data when resend type is set to interval. + /// + private uint _nextIntervalResend; + /// + /// Number of resends remaining when the object has not changed. + /// + private ushort _resendsRemaining; + /// + /// True if object was changed previous tick. + /// + private bool _previouslyChanged; + /// + /// Animators found on the graphical object. + /// + private Animator[] _graphicalAnimators; + /// + /// True if GraphicalAniamtors have been intialized. + /// + private bool _animatorsInitialized; + /// + /// Tick on the last received state. + /// + private uint _lastStateLocalTick; + /// + /// True if a connection is owner and prediction methods are implemented. + /// + private bool _isPredictingOwner(NetworkConnection c) => (c == base.Owner && _implementsPredictionMethods); + /// + /// Current interpolation value. + /// + private long _currentSpectatorInterpolation; + /// + /// Target interpolation when collision is exited. + /// + private uint _targetSpectatorInterpolation; + /// + /// Target interpolation when collision is entered. + /// + private uint _targetCollisionSpectatorInterpolation; + /// + /// How much per tick to decrease to collision interpolation when colliding with a local player object. + /// + private byte _interpolationDecreaseStep; + /// + /// How much per tick to increase to normal interpolation when not colliding with a local player object. + /// + private byte _interpolationIncreaseStep; + /// + /// Last local tick that collision has stayed with local client objects. + /// + private uint _collisionStayedTick; + /// + /// Local client objects this object is currently colliding with. + /// + private HashSet _localClientCollidedObjects = new HashSet(); + /// + /// True if spectator prediction is paused. + /// + private bool _spectatorPaused; + ///// + ///// Target number of ticks to ignore when replaying. + ///// + //private uint _ignoredTicks; + #region Smoothing datas. + private static SmoothingData _accurateSmoothingData = new SmoothingData() + { + InterpolationPercent = 0.5f, + CollisionInterpolationPercent = 0.05f, + InterpolationDecreaseStep = 1, + InterpolationIncreaseStep = 2, + }; + private static SmoothingData _mixedSmoothingData = new SmoothingData() + { + InterpolationPercent = 1f, + CollisionInterpolationPercent = 0.1f, + InterpolationDecreaseStep = 1, + InterpolationIncreaseStep = 3, + }; + private static SmoothingData _gradualSmoothingData = new SmoothingData() + { + InterpolationPercent = 1.5f, + CollisionInterpolationPercent = 0.2f, + InterpolationDecreaseStep = 1, + InterpolationIncreaseStep = 5, + }; + #endregion + #endregion + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Rigidbodies_OnSpawnServer(NetworkConnection c) + { + if (!IsRigidbodyPrediction) + return; + if (c == base.Owner) + return; + if (c.IsLocalClient) + return; + + uint tick = c.LastPacketTick; + if (_predictionType == PredictionType.Rigidbody) + SendRigidbodyState(tick, c, true); + else + SendRigidbody2DState(tick, c, true); + } + + /// + /// Called when the client starts. + /// + private void Rigidbodies_OnStartClient() + { + //Store up to 1 second of states. + int capacity = base.TimeManager.TickRate; + /* Only need to check one collection capacity since they both will be the same. + * If capacity does not line up then re-initialize. */ + if (capacity != _rigidbodyStates.Capacity) + { + _rigidbodyStates.Initialize(capacity); + _rigidbody2dStates.Initialize(capacity); + } + } + + /// + /// Called on client when ownership changes for this object. + /// + /// + private void Rigidbodies_OnOwnershipClient(NetworkConnection prevOwner) + { + if (!IsRigidbodyPrediction) + return; + //If owner no need to fix for animators. + if (base.IsOwner) + return; + //Would have already fixed if animators are set. + if (_animatorsInitialized) + return; + + _animatorsInitialized = true; + _graphicalAnimators = _graphicalObject.GetComponentsInChildren(true); + + if (_graphicalAnimators.Length > 0) + { + for (int i = 0; i < _graphicalAnimators.Length; i++) + _graphicalAnimators[i].keepAnimatorStateOnDisable = true; + + /* True if at least one animator is on the graphical root. + * Unity gets components in order so it's safe to assume + * 0 would be the topmost animator. This has to be done + * to prevent animation jitter when pausing the rbs. */ + if (_graphicalAnimators[0].transform == _graphicalObject) + { + Transform graphicalHolder = new GameObject().transform; + graphicalHolder.name = "GraphicalObjectHolder"; + graphicalHolder.SetParent(transform); + graphicalHolder.localPosition = _graphicalInstantiatedOffsetPosition; + graphicalHolder.localRotation = _graphicalInstantiatedOffsetRotation; + graphicalHolder.localScale = _graphicalObject.localScale; + _graphicalObject.SetParent(graphicalHolder); + _graphicalObject.localPosition = Vector3.zero; + _graphicalObject.localRotation = Quaternion.identity; + _graphicalObject.localScale = Vector3.one; + SetGraphicalObject(graphicalHolder); + } + } + } + + /// + /// Called after a tick occurs; physics would have simulated if using PhysicsMode.TimeManager. + /// + private void Rigidbodies_TimeManager_OnPostTick() + { + if (!IsRigidbodyPrediction) + return; + if (base.IsServer) + return; + + bool is2D = (_predictionType == PredictionType.Rigidbody2D); + TrySetCollisionExited(is2D); + + /* Can check either one. They may not be initialized yet if host. */ + if (_rigidbodyStates.Initialized) + { + if (_localTick == 0) + _localTick = base.TimeManager.LocalTick; + + if (!is2D) + _rigidbodyStates.Add(new RigidbodyState(_rigidbody, _localTick)); + else + _rigidbody2dStates.Add(new Rigidbody2DState(_rigidbody2d, _localTick)); + } + + if (CanPredict()) + { + UpdateSpectatorSmoothing(); + if (!is2D) + PredictVelocity(gameObject.scene.GetPhysicsScene()); + else + PredictVelocity(gameObject.scene.GetPhysicsScene2D()); + } + } + + /// + /// Unsets collision values if collision was known to be entered but there are no longer any contact points. + /// + private void TrySetCollisionExited(bool is2d) + { + /* If this object is no longer + * colliding with local client objects + * then unset collision. + * This is done here instead of using + * OnCollisionExit because often collisionexit + * will be missed due to ignored ticks. + * While not ignoring ticks is always an option + * its not ideal because ignoring ticks helps + * prevent over predicting. */ + if (_collisionStayedTick != 0 && (base.TimeManager.LocalTick != _collisionStayedTick)) + CollisionExited(); + } + + /// + /// Called before performing a reconcile on NetworkBehaviour. + /// + private void Rigidbodies_TimeManager_OnPreReconcile(NetworkBehaviour nb) + { + /* Exit if owner and implements prediction methods + * because csp would be handled by prediction methods + * rather than predicted object. */ + if (IsPredictingOwner()) + return; + if (nb.gameObject == gameObject) + return; + if (!IsRigidbodyPrediction) + return; + + bool is2D = (_predictionType == PredictionType.Rigidbody2D); + uint lastNbTick = nb.GetLastReconcileTick(); + int stateIndex = GetCachedStateIndex(lastNbTick, is2D); + + /* If running again on the same reconcile or state is for a different + * tick then do make RBs kinematic. Resetting to a different state + * could cause a desync and there's no reason to run the same + * tick twice. */ + if (stateIndex == -1) + { + _spectatorSmoother?.SetLocalReconcileTick(-1); + _rigidbodyPauser.Pause(); + } + //If state was found then reset to it. + else + { + _spectatorSmoother?.SetLocalReconcileTick(lastNbTick); + if (is2D) + { + _rigidbody2dStates.RemoveRange(true, stateIndex); + ResetRigidbody2DToData(_rigidbody2dStates[0]); + } + else + { + _rigidbodyStates.RemoveRange(true, stateIndex); + ResetRigidbodyToData(_rigidbodyStates[0]); + } + } + } + + /// + /// Called after performing a reconcile on NetworkBehaviour. + /// + private void Rigidbodies_TimeManager_OnPostReconcile(NetworkBehaviour nb) + { + _rigidbodyPauser.Unpause(); + } + + /// + /// Called before physics is simulated when replaying a replicate method. + /// Contains the PhysicsScene and PhysicsScene2D which was simulated. + /// + private void Rigidbodies_PredictionManager_OnPreReplicateReplay(uint tick, PhysicsScene ps, PhysicsScene2D ps2d) + { + if (!CanPredict()) + return; + + //if (_localTick - tick < _ignoredTicks) + // _rigidbodyPauser.Pause(); + + if (_predictionType == PredictionType.Rigidbody) + { + _preReplicateReplayCacheIndex = GetCachedStateIndex(tick, false); + if (_preReplicateReplayCacheIndex != -1) + { + bool prevKinematic = _rigidbodyStates[_preReplicateReplayCacheIndex].IsKinematic; + _rigidbody.isKinematic = prevKinematic; + } + PredictVelocity(ps); + } + else if (_predictionType == PredictionType.Rigidbody2D) + { + _preReplicateReplayCacheIndex = GetCachedStateIndex(tick, true); + if (_preReplicateReplayCacheIndex != -1) + { + bool prevSimulated = _rigidbody2dStates[_preReplicateReplayCacheIndex].Simulated; + _rigidbody2d.simulated = prevSimulated; + _rigidbody2d.isKinematic = !prevSimulated; + } + PredictVelocity(ps2d); + } + } + + /// + /// Called before physics is simulated when replaying a replicate method. + /// Contains the PhysicsScene and PhysicsScene2D which was simulated. + /// + private void Rigidbodies_PredictionManager_OnPostReplicateReplay(uint tick, PhysicsScene ps, PhysicsScene2D ps2d) + { + if (!CanPredict()) + return; + if (_rigidbodyPauser.Paused) + return; + + if (_predictionType == PredictionType.Rigidbody) + { + int index = _preReplicateReplayCacheIndex; + if (index != -1) + { + bool prevKinematic = _rigidbodyStates[index].IsKinematic; + _rigidbodyStates[index] = new RigidbodyState(_rigidbody, prevKinematic, tick); + } + } + if (_predictionType == PredictionType.Rigidbody2D) + { + int index = GetCachedStateIndex(tick, true); + if (index != -1) + { + bool prevSimulated = _rigidbody2dStates[index].Simulated; + _rigidbody2dStates[index] = new Rigidbody2DState(_rigidbody2d, prevSimulated, tick); + } + } + } + + /// + /// Pauses corrections as a spectator object. + /// + public void SetPauseSpectatorCorrections_Experimental(bool pause) + { + _spectatorPaused = pause; + if (pause) + { + _rigidbodyStates.Clear(); + _rigidbody2dStates.Clear(); + } + } + + /// + /// Called when ping updates for the local client. + /// + private void Rigidbodies_OnRoundTripTimeUpdated(long ping) + { + /* Only update periodically when ping changes. + * This is to prevent excessive interpolation + * changes. */ + ulong difference = (ulong)Mathf.Abs(ping - _lastPing); + //Allow update if ping jump is large enough. + if (difference < 50) + { + uint tickInterval = base.TimeManager.TimeToTicks(5f, Managing.Timing.TickRounding.RoundUp); + if (base.TimeManager.LocalTick - _lastPingUpdateTick < tickInterval) + return; + } + SetTargetSmoothing(ping, false); + } + /// + /// Sets target smoothing values. + /// + /// True to set current values to targets immediately. + private void SetTargetSmoothing(long ping, bool setImmediately) + { + if (_spectatorSmoother == null) + return; + + _lastPingUpdateTick = base.TimeManager.LocalTick; + _lastPing = ping; + SetValues(); + //Ignored ticks will be less for predicted spawner. + //if (base.NetworkObject.PredictedSpawner.IsLocalClient) + // _ignoredTicks /= 2; + + //_igtt = _ignoredTicks; + //_ignoredTicks = 0; + //if (base.Owner.IsValid && (base.Owner != base.NetworkObject.PredictedSpawner)) + //{ + // _ignoredTicks *= 4; + // if (gameObject.name.Contains("Bullet")) + // Debug.Log("Setting to " + _ignoredTicks); + //} + //if (base.Owner.IsValid && (base.Owner == base.NetworkObject.PredictedSpawner))// base.IsOwner) + // _ignoredTicks = 0; + + //_spectatorSmoother.SetIgnoredTicks(_ignoredTicks); + + //If to apply values to targets immediately. + if (setImmediately) + { + _currentSpectatorInterpolation = (CollidingWithLocalClient()) ? _targetCollisionSpectatorInterpolation : _targetSpectatorInterpolation; + _spectatorSmoother.SetInterpolation((uint)_currentSpectatorInterpolation); + } + + //Sets ranges to use based on smoothing type. + void SetValues() + { + SmoothingData data; + if (_spectatorSmoothingType == SpectatorSmoothingType.Accuracy) + data = _accurateSmoothingData; + else if (_spectatorSmoothingType == SpectatorSmoothingType.Mixed) + data = _mixedSmoothingData; + else if (_spectatorSmoothingType == SpectatorSmoothingType.Gradual) + data = _gradualSmoothingData; + else + data = _customSmoothingData; + + TimeManager tm = base.TimeManager; + double interpolationTime = (ping / 1000d) * data.InterpolationPercent; + _targetSpectatorInterpolation = tm.TimeToTicks(interpolationTime, TickRounding.RoundUp); + double collisionInterpolationTime = (ping / 1000d) * data.CollisionInterpolationPercent; + _targetCollisionSpectatorInterpolation = tm.TimeToTicks(collisionInterpolationTime, TickRounding.RoundUp); + + _interpolationDecreaseStep = data.InterpolationDecreaseStep; + _interpolationIncreaseStep = data.InterpolationIncreaseStep; + } + } + + /// + /// Returns if this object is colliding with any local client objects. + /// + /// + private bool CollidingWithLocalClient() + { + /* If it's been more than 1 tick since collision stayed + * then do not consider as collided. */ + return (base.TimeManager.LocalTick - _collisionStayedTick) < 1; + } + + private uint _igtt; + /// + /// Updates spectator smoothing values to move towards their targets. + /// + private void UpdateSpectatorSmoothing() + { + bool colliding = CollidingWithLocalClient(); + if (colliding) + _currentSpectatorInterpolation -= _interpolationDecreaseStep; + else + _currentSpectatorInterpolation += _interpolationIncreaseStep; + + _currentSpectatorInterpolation = (long)Mathf.Clamp(_currentSpectatorInterpolation, _targetCollisionSpectatorInterpolation, _targetSpectatorInterpolation); + _spectatorSmoother.SetInterpolation((uint)_currentSpectatorInterpolation); + } + + /// + /// Called when a collision occurs and the smoothing type must perform operations. + /// + private bool CollisionEnteredLocalClientObject(GameObject go) + { + if (go.TryGetComponent(out NetworkObject nob)) + return nob.Owner.IsLocalClient; + + //Fall through. + return false; + } + + /// + /// Sends the rigidbodies state to Observers of a NetworkBehaviour. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SendRigidbodyState(NetworkBehaviour nb) + { + NetworkConnection owner = nb.Owner; + if (!owner.IsActive) + return; + NetworkManager nm = nb.NetworkManager; + if (nm == null) + return; + + uint tick = nb.GetLastReplicateTick(); + TrySendRigidbodyState(nb, tick); + } + + /// + /// Send current state to a connection. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void TrySendRigidbodyState(NetworkBehaviour nb, uint tick) + { + if (!IsRigidbodyPrediction) + return; + NetworkConnection nbOwner = nb.Owner; + //No need to send to self unless doesnt implement prediction methods. + if (_isPredictingOwner(nbOwner)) + return; + //If clientHost. + if (nbOwner.IsLocalClient) + return; + /* Not an observer. SendTargetRpc normally + * already checks this when ValidateTarget + * is true but we want to save perf by exiting + * early before checks and serialization when + * we know the conn is not an observer. */ + if (!base.Observers.Contains(nbOwner)) + return; + + bool hasChanged = base.TransformMayChange(); + if (!hasChanged) + { + //Not changed but was previous tick. Reset resends. + if (_previouslyChanged) + _resendsRemaining = base.TimeManager.TickRate; + + uint currentTick = base.TimeManager.Tick; + //Resends remain. + if (_resendsRemaining > 0) + { + _resendsRemaining--; + //If now 0 then update next send interval. + if (_resendsRemaining == 0) + UpdateNextIntervalResend(); + } + //No more resends. + else + { + //No resend interval. + if (_resendType == ResendType.Disabled) + return; + //Interval not yet met. + if (currentTick < _nextIntervalResend) + return; + + UpdateNextIntervalResend(); + } + + //Updates the next tick when a resend should occur. + void UpdateNextIntervalResend() + { + _nextIntervalResend = (currentTick + _resendInterval); + } + + } + _previouslyChanged = hasChanged; + + if (_predictionType == PredictionType.Rigidbody) + SendRigidbodyState(tick, nbOwner, false); + else + SendRigidbody2DState(tick, nbOwner, false); + } + + /// + /// Gets a cached state index in actual array position. + /// + /// + private int GetCachedStateIndex(uint tick, bool is2d) + { + int count; + uint firstTick; + //3d. + if (!is2d) + { + count = _rigidbodyStates.Count; + if (count == 0) + return -1; + firstTick = _rigidbodyStates[0].LocalTick; + } + //2d. + else + { + count = _rigidbody2dStates.Count; + if (count == 0) + return -1; + firstTick = _rigidbody2dStates[0].LocalTick; + } + + //First tick is higher than current, no match is possibloe. + if (firstTick > tick) + return -1; + + long difference = (tick - firstTick); + //Desired tick would be out of bounds. This should never happen. + if (difference >= count) + return -1; + + return (int)difference; + } + + + /// + /// Tries to predict velocity for a Vector3. + /// + protected bool PredictVector3Velocity(ref float? velocityBaseline, ref Vector3 lastVelocity, Vector3 velocity, out Vector3 result) + { + float velocityDifference; + float directionDifference; + + /* Velocity. */ + directionDifference = (velocityBaseline != null) ? + Vector3.SqrMagnitude(lastVelocity.normalized - velocity.normalized) : + 0f; + //If direction has changed too much then reset the baseline. + if (directionDifference > 0.01f) + { + velocityBaseline = null; + } + //Direction hasn't changed enough to reset baseline. + else + { + //Difference in velocity since last simulation. + velocityDifference = Vector3.Magnitude(lastVelocity - velocity); + //If there is no baseline. + if (velocityBaseline == null) + { + if (velocityDifference > 0) + velocityBaseline = velocityDifference; + } + //If there is a baseline. + else + { + //If the difference exceeds the baseline by 10% then reset baseline so another will be calculated. + if (velocityDifference > (velocityBaseline.Value * 1.1f) || velocityDifference < (velocityBaseline.Value * 0.9f)) + { + velocityBaseline = null; + } + //Velocity difference is close enough to the baseline to where it doesn't need to be reset, so use prediction. + else + { + Vector3 changeMultiplied = (velocity - lastVelocity) * _maintainedVelocity; + //Retaining velocity. + if (_maintainedVelocity > 0f) + { + result = (velocity + changeMultiplied); + } + //Reducing velocity. + else + { + result = (velocity + changeMultiplied); + /* When reducing velocity make sure the direction + * did not change. When this occurs it means the velocity + * was reduced into the opposite direction. To prevent + * this from happening just zero out velocity instead. */ + if (velocity.normalized != result.normalized) + result = Vector3.zero; + } + return true; + } + } + } + + //Fall through. + result = Vector3.zero; + return false; + } + + + /// + /// Tries to predict velocity for a float. + /// + private bool PredictFloatVelocity(ref float? velocityBaseline, ref float lastVelocity, float velocity, out float result) + { + float velocityDifference; + float directionDifference; + + /* Velocity. */ + directionDifference = (velocityBaseline != null) ? (velocity - lastVelocity) : 0f; + + //If direction has changed too much then reset the baseline. + if (directionDifference > 0.01f) + { + velocityBaseline = null; + } + //Direction hasn't changed enough to reset baseline. + else + { + //Difference in velocity since last simulation. + velocityDifference = Mathf.Abs(lastVelocity - velocity); + //If there is no baseline. + if (velocityBaseline == null) + { + if (velocityDifference > 0) + velocityBaseline = velocityDifference; + } + //If there is a baseline. + else + { + //If the difference exceeds the baseline by 10% then reset baseline so another will be calculated. + if (velocityDifference > (velocityBaseline.Value * 1.1f) || velocityDifference < (velocityBaseline.Value * 0.9f)) + { + velocityBaseline = null; + } + //Velocity difference is close enough to the baseline to where it doesn't need to be reset, so use prediction. + else + { + float changeMultiplied = (velocity - lastVelocity) * _maintainedVelocity; + //Retaining velocity. + if (_maintainedVelocity > 0f) + { + result = (velocity + changeMultiplied); + } + //Reducing velocity. + else + { + result = (velocity + changeMultiplied); + /* When reducing velocity make sure the direction + * did not change. When this occurs it means the velocity + * was reduced into the opposite direction. To prevent + * this from happening just zero out velocity instead. */ + if (Mathf.Abs(velocity) != Mathf.Abs(result)) + result = 0f; + } + return true; + } + } + } + + //Fall through. + result = 0f; + return false; + } + + /// + /// Returns if prediction can be used on this rigidbody. + /// + /// + private bool CanPredict() + { + if (!IsRigidbodyPrediction) + return false; + if (base.IsServer || IsPredictingOwner()) + return false; + if (_spectatorPaused) + return false; + + return true; + } + #endregion + + #region Rigidbody. + #region Private. + /// + /// Past RigidbodyStates. + /// + private RingBuffer _rigidbodyStates = new RingBuffer(); + /// + /// Velocity from previous simulation. + /// + private Vector3 _lastVelocity; + /// + /// Angular velocity from previous simulation. + /// + private Vector3 _lastAngularVelocity; + /// + /// Baseline for velocity magnitude. + /// + private float? _velocityBaseline; + /// + /// Baseline for angular velocity magnitude. + /// + private float? _angularVelocityBaseline; + /// + /// PhysicsScene for this object when OnPreReconcile is called. + /// + private PhysicsScene _physicsScene; + #endregion + + private void OnCollisionEnter(Collision collision) + { + if (_predictionType != PredictionType.Rigidbody) + return; + + GameObject go = collision.gameObject; + if (CollisionEnteredLocalClientObject(go)) + CollisionEntered(go); + } + + + private void OnCollisionStay(Collision collision) + { + if (_predictionType != PredictionType.Rigidbody) + return; + + if (_localClientCollidedObjects.Contains(collision.gameObject)) + _collisionStayedTick = base.TimeManager.LocalTick; + } + + /// + /// Resets the rigidbody to a state. + /// + private void ResetRigidbodyToData(RigidbodyState state) + { + //Update transform and rigidbody. + _rigidbody.transform.position = state.Position; + _rigidbody.transform.rotation = state.Rotation; + bool isKinematic = state.IsKinematic; + _rigidbody.isKinematic = isKinematic; + if (!isKinematic) + { + _rigidbody.velocity = state.Velocity; + _rigidbody.angularVelocity = state.AngularVelocity; + } + + /* Do not need to sync transforms because it's done internally by the reconcile method. + * That is, so long as this is called using OnPreReconcile. */ + + //Set prediction defaults. + _velocityBaseline = null; + _angularVelocityBaseline = null; + _lastVelocity = _rigidbody.velocity; + _lastAngularVelocity = _rigidbody.angularVelocity; + } + + /// + /// Sets the next predicted velocity on the rigidbody. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void PredictVelocity(PhysicsScene ps) + { + if (_maintainedVelocity == 0f) + return; + if (ps != _physicsScene) + return; + + Vector3 result; + if (PredictVector3Velocity(ref _velocityBaseline, ref _lastVelocity, _rigidbody.velocity, out result)) + _rigidbody.velocity = result; + if (PredictVector3Velocity(ref _angularVelocityBaseline, ref _lastAngularVelocity, _rigidbody.angularVelocity, out result)) + _rigidbody.angularVelocity = result; + + _lastVelocity = _rigidbody.velocity; + _lastAngularVelocity = _rigidbody.angularVelocity; + } + + + /// + /// Sends current states of this object to client. + /// + private void SendRigidbodyState(uint reconcileTick, NetworkConnection conn, bool applyImmediately) + { + //No need to send to owner if they implement prediction methods. + if (_isPredictingOwner(conn)) + return; + reconcileTick = (conn == base.NetworkObject.PredictedSpawner) ? conn.LastPacketTick : reconcileTick; + RigidbodyState state = new RigidbodyState(_rigidbody, reconcileTick); + TargetSendRigidbodyState(conn, state, applyImmediately); + } + + /// + /// Sends transform and rigidbody state to spectators. + /// + [TargetRpc(ValidateTarget = false)] + private void TargetSendRigidbodyState(NetworkConnection c, RigidbodyState state, bool applyImmediately, Channel channel = Channel.Unreliable) + { + if (!CanPredict()) + return; + + uint localTick = state.LocalTick; + if (applyImmediately) + { + /* If PredictedSpawner is self then this client + * was the one to predicted spawn this object. When that is + * the case do not apply initial velocities, but so allow + * regular updates/corrections. */ + if (base.NetworkObject.PredictedSpawner.IsLocalClient) + return; + } + else + { + if (!CanProcessReceivedState(localTick)) + return; + } + + if (applyImmediately) + { + _rigidbodyStates.Clear(); + ResetRigidbodyToData(state); + } + else + { + int index = GetCachedStateIndex(localTick, false); + if (index != -1) + _rigidbodyStates[index] = state; + else + _rigidbodyStates.Add(state); + } + } + #endregion + + #region Rigidbody2D. + #region Private. + /// + /// Past RigidbodyStates. + /// + private RingBuffer _rigidbody2dStates = new RingBuffer(); + /// + /// Velocity from previous simulation. + /// + private Vector3 _lastVelocity2D; + /// + /// Angular velocity from previous simulation. + /// + private float _lastAngularVelocity2D; + /// + /// Baseline for velocity magnitude. + /// + private float? _velocityBaseline2D; + /// + /// Baseline for angular velocity magnitude. + /// + private float? _angularVelocityBaseline2D; + /// + /// PhysicsScene for this object when OnPreReconcile is called. + /// + private PhysicsScene2D _physicsScene2D; + /// + /// Last found cacheIndex during PreReplay. + /// + private int _preReplicateReplayCacheIndex; + /// + /// Last tick a ping update was received. + /// + private uint _lastPingUpdateTick; + /// + /// Last ping during a ping update. + /// + private long _lastPing; + #endregion + + private void OnCollisionEnter2D(Collision2D collision) + { + if (_predictionType != PredictionType.Rigidbody2D) + return; + + GameObject go = collision.gameObject; + if (CollisionEnteredLocalClientObject(go)) + CollisionEntered(go); + } + + private void OnCollisionStay2D(Collision2D collision) + { + if (_predictionType != PredictionType.Rigidbody2D) + return; + + if (_localClientCollidedObjects.Contains(collision.gameObject)) + _collisionStayedTick = base.TimeManager.LocalTick; + } + + /// + /// Called when collision has entered a local clients object. + /// + private void CollisionEntered(GameObject go) + { + _collisionStayedTick = base.TimeManager.LocalTick; + _localClientCollidedObjects.Add(go); + } + + /// + /// Called when collision has exited a local clients object. + /// + private void CollisionExited() + { + _localClientCollidedObjects.Clear(); + _collisionStayedTick = 0; + } + + /// + /// Resets the Rigidbody2D to last received data. + /// + private void ResetRigidbody2DToData(Rigidbody2DState state) + { + //Update transform and rigidbody. + _rigidbody2d.transform.position = state.Position; + _rigidbody2d.transform.rotation = state.Rotation; + bool simulated = state.Simulated; + _rigidbody2d.simulated = simulated; + _rigidbody2d.isKinematic = !simulated; + if (simulated) + { + _rigidbody2d.velocity = state.Velocity; + _rigidbody2d.angularVelocity = state.AngularVelocity; + } + + /* Do not need to sync transforms because it's done internally by the reconcile method. + * That is, so long as this is called using OnPreReconcile. */ + + //Set prediction defaults. + _velocityBaseline2D = null; + _angularVelocityBaseline2D = null; + _lastVelocity2D = _rigidbody2d.velocity; + _lastAngularVelocity2D = _rigidbody2d.angularVelocity; + } + + /// + /// Sets the next predicted velocity on the rigidbody. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void PredictVelocity(PhysicsScene2D ps) + { + if (_maintainedVelocity == 0f) + return; + if (ps != _physicsScene2D) + return; + + Vector3 v3Result; + if (PredictVector3Velocity(ref _velocityBaseline2D, ref _lastVelocity2D, _rigidbody2d.velocity, out v3Result)) + _rigidbody2d.velocity = v3Result; + float floatResult; + if (PredictFloatVelocity(ref _angularVelocityBaseline2D, ref _lastAngularVelocity2D, _rigidbody2d.angularVelocity, out floatResult)) + _rigidbody2d.angularVelocity = floatResult; + + _lastVelocity2D = _rigidbody2d.velocity; + _lastAngularVelocity2D = _rigidbody2d.angularVelocity; + } + + + /// + /// Sends current Rigidbody2D state to a connection. + /// + private void SendRigidbody2DState(uint reconcileTick, NetworkConnection conn, bool applyImmediately) + { + Rigidbody2DState state = new Rigidbody2DState(_rigidbody2d, reconcileTick); + TargetSendRigidbody2DState(conn, state, applyImmediately); + } + + /// + /// Sends transform and rigidbody state to spectators. + /// + [TargetRpc(ValidateTarget = false)] + private void TargetSendRigidbody2DState(NetworkConnection c, Rigidbody2DState state, bool applyImmediately, Channel channel = Channel.Unreliable) + { + if (!CanPredict()) + return; + + uint localTick = state.LocalTick; + if (applyImmediately) + { + /* If PredictedSpawner is self then this client + * was the one to predicted spawn this object. When that is + * the case do not apply initial velocities, but so allow + * regular updates/corrections. */ + if (base.NetworkObject.PredictedSpawner.IsLocalClient) + return; + } + else + { + if (!CanProcessReceivedState(localTick)) + return; + } + + if (applyImmediately) + { + _rigidbody2dStates.Clear(); + ResetRigidbody2DToData(state); + } + else + { + int index = GetCachedStateIndex(localTick, true); + if (index != -1) + _rigidbody2dStates[index] = state; + else + _rigidbody2dStates.Add(state); + } + + + } + + /// + /// Returns if a received state can be processed based on it's tick. + /// + /// + /// + private bool CanProcessReceivedState(uint stateTick) + { + //Older than another received value. + if (stateTick <= _lastStateLocalTick) + return false; + _lastStateLocalTick = stateTick; + + return true; + } + #endregion + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObject.Rigidbodies.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObject.Rigidbodies.cs.meta new file mode 100644 index 0000000..0d5f06d --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObject.Rigidbodies.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6bd787cd0da2e9e4ab4bd30794ff0082 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObject.cs b/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObject.cs new file mode 100644 index 0000000..bdf3ff5 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObject.cs @@ -0,0 +1,605 @@ +using FishNet.Component.Transforming; +using FishNet.Utility.Extension; +using FishNet.Connection; +using FishNet.Managing; +using FishNet.Object; +using System; +using UnityEngine; + +namespace FishNet.Component.Prediction +{ + [AddComponentMenu("FishNet/Component/PredictedObject")] + public partial class PredictedObject : NetworkBehaviour + { + #region Types. + /// + /// How to favor smoothing for predicted objects. + /// + public enum SpectatorSmoothingType + { + /// + /// Favor accurate collisions. With fast moving objects this may result in some jitter with higher latencies. + /// + Accuracy = 0, + /// + /// A mix between Accuracy and Smoothness. + /// + Mixed = 1, + /// + /// Prefer smooth movement and corrections. Fast moving objects may collide before the graphical representation catches up. + /// + Gradual = 2, + /// + /// Configure values to your preference. + /// + Custom = 3, + } + /// + /// State of this object in a collision. + /// + private enum CollectionState : byte + { + Unset = 0, + Added = 1, + Removed = 2, + } + /// + /// Type of prediction movement being used. + /// + internal enum PredictionType : byte + { + Other = 0, + Rigidbody = 1, + Rigidbody2D = 2 + } + internal enum ResendType : byte + { + Disabled = 0, + Interval = 1, + } + #endregion + + #region Public. + /// + /// True if the prediction type is for a rigidbody. + /// + public bool IsRigidbodyPrediction => (_predictionType == PredictionType.Rigidbody || _predictionType == PredictionType.Rigidbody2D); + #endregion + + #region Serialized. + /// + /// True if this object implements replicate and reconcile methods. + /// + [Tooltip("True if this object implements replicate and reconcile methods.")] + [SerializeField] + private bool _implementsPredictionMethods = true; + /// + /// Transform which holds the graphical features of this object. This transform will be smoothed when desynchronizations occur. + /// + [Tooltip("Transform which holds the graphical features of this object. This transform will be smoothed when desynchronizations occur.")] + [SerializeField] + private Transform _graphicalObject; + /// + /// Gets GraphicalObject. + /// + public Transform GetGraphicalObject() => _graphicalObject; + /// + /// Sets GraphicalObject. + /// + /// + public void SetGraphicalObject(Transform value) + { + _graphicalObject = value; + SetInstantiatedOffsetValues(); + _spectatorSmoother?.SetGraphicalObject(value); + _ownerSmoother?.SetGraphicalObject(value); + } + /// + /// True to enable teleport threshhold. + /// + [Tooltip("True to enable teleport threshhold.")] + [SerializeField] + private bool _enableTeleport; + /// + /// How far the transform must travel in a single update to cause a teleport rather than smoothing. Using 0f will teleport every update. + /// + [Tooltip("How far the transform must travel in a single update to cause a teleport rather than smoothing. Using 0f will teleport every update.")] + [Range(0f, 200f)] //Unity bug? Values ~over 200f lose decimal display within inspector. + [SerializeField] + private float _teleportThreshold = 1f; + /// + /// Gets the value for SmoothTicks. + /// + /// + [Obsolete("No longer used. This setting has been replaced by Smoothing Type.")]//Remove on 2023/06/01 + public bool GetSmoothTicks() => true; + /// + /// Sets the value for SmoothTicks. + /// + /// + /// + [Obsolete("No longer used. This setting has been replaced by Smoothing Type.")] //Remove on 2023/06/01 + public void SetSmoothTicks(bool value) { } + /// + /// True to smooth position on owner objects. + /// + [Tooltip("True to smooth position on owner objects.")] + [SerializeField] + private bool _ownerSmoothPosition = true; + /// + /// True to smooth rotation on owner objects. + /// + [Tooltip("True to smooth rotation on owner objects.")] + [SerializeField] + private bool _ownerSmoothRotation = true; + /// + /// How far in the past to keep the graphical object when owner. Using a value of 0 will disable interpolation. + /// + [Tooltip("How far in the past to keep the graphical object when owner. Using a value of 0 will disable interpolation.")] + [Range(0, 255)] + [SerializeField] + private byte _ownerInterpolation = 1; + /// + /// Gets the iterpolation value to use when the owner of this object. + /// + /// True to get the interpolation for when owner, false to get the interpolation for when a spectator. + [Obsolete("No longer used. This setting has been replaced by Smoothing Type.")]//Remove on 2023/06/01 + public byte GetInterpolation(bool asOwner) => 0; + /// + /// Sets the interpolation value to use when the owner of this object. + /// + /// + /// True to set the interpolation for when owner, false to set interpolation for when a spectator. + [Obsolete("No longer used. This setting has been replaced by Smoothing Type.")]//Remove on 2023/06/01 + public void SetInterpolation(byte value, bool asOwner) + { + //if (asOwner) + //{ + // _ownerInterpolation = value; + // _ownerSmoother?.SetInterpolation(value); + //} + //else + //{ + // _spectatorInterpolation = value; + // _spectatorSmoother?.SetInterpolation(value); + //} + } + /// + /// Type of prediction movement which is being used. + /// + [Tooltip("Type of prediction movement which is being used.")] + [SerializeField] + private PredictionType _predictionType; + /// + /// Rigidbody to predict. + /// + [Tooltip("Rigidbody to predict.")] + [SerializeField] + private Rigidbody _rigidbody; + /// + /// Rigidbody2D to predict. + /// + [Tooltip("Rigidbody2D to predict.")] + [SerializeField] + private Rigidbody2D _rigidbody2d; + /// + /// True to smooth position on spectated objects. + /// + [Tooltip("True to smooth position on spectated objects.")] + [SerializeField] + private bool _spectatorSmoothPosition = true; + /// + /// True to smooth rotation on spectated objects. + /// + [Tooltip("True to smooth rotation on spectated objects.")] + [SerializeField] + private bool _spectatorSmoothRotation = true; + /// + /// How to favor smoothing for predicted objects. + /// + [Tooltip("How to favor smoothing for predicted objects.")] + [SerializeField] + private SpectatorSmoothingType _spectatorSmoothingType = SpectatorSmoothingType.Mixed; + /// + /// Custom settings for smoothing data. + /// + [Tooltip("Custom settings for smoothing data.")] + [SerializeField] + private SmoothingData _customSmoothingData = _mixedSmoothingData; + /// + /// Preview of selected preconfigured smoothing data. This is only used for the inspector. + /// + [SerializeField] + private SmoothingData _preconfiguredSmoothingDataPreview = _mixedSmoothingData; + /// + /// Sets SpectactorSmoothingType value. + /// + /// Value to use. + public void SetSpectatorSmoothingType(SpectatorSmoothingType value) + { + if (base.IsSpawned) + base.NetworkManager.LogWarning($"Spectator smoothing type may only be set before the object is spawned, such as after instantiating but before spawning."); + else + _spectatorSmoothingType = value; + } + + ///// + ///// How far in the past to keep the graphical object when not owner. Using a value of 0 will disable interpolation. + ///// + //[Tooltip("How far in the past to keep the graphical object when not owner. Using a value of 0 will disable interpolation.")] + //[Range(0, 255)] + //[SerializeField] + //private byte _spectatorInterpolation = 4; + ///// + ///// Multiplier to apply to movement speed when buffer is over interpolation. + ///// + //[Tooltip("Multiplier to apply to movement speed when buffer is over interpolation.")] + //[Range(0f, 5f)] + //[SerializeField] + //private float _overflowMultiplier = 0.1f; + /// + /// Multiplier applied to difference in velocity between ticks. + /// Positive values will result in more velocity while lowers will result in less. + /// A value of 1f will prevent any velocity from being lost between ticks, unless indicated by the server. + /// + [Tooltip("Multiplier applied to difference in velocity between ticks. Positive values will result in more velocity while lowers will result in less. A value of 1f will prevent any velocity from being lost between ticks, unless indicated by the server.")] + [Range(-10f, 10f)] + [SerializeField] + private float _maintainedVelocity = 0f; + /// + /// How often to resend current values regardless if the state has changed. Using this value will consume more bandwidth but may be preferred if you want to force synchronization the object move on the client but not on the server. + /// + [Tooltip("How often to resend current values regardless if the state has changed. Using this value will consume more bandwidth but may be preferred if you want to force synchronization the object move on the client but not on the server.")] + [SerializeField] + private ResendType _resendType = ResendType.Disabled; + /// + /// How often in ticks to resend values. + /// + [Tooltip("How often in ticks to resend values.")] + [SerializeField] + private ushort _resendInterval = 30; + /// + /// NetworkTransform to configure. + /// + [Tooltip("NetworkTransform to configure.")] + [SerializeField] + private NetworkTransform _networkTransform; + #endregion + + #region Private. + /// + /// True if client subscribed to events. + /// + private bool _clientSubscribed; + /// + /// True if this PredictedObject has been registered with the PredictionManager. + /// + private bool _registered; + /// + /// GraphicalObject position difference from this object when this is instantiated. + /// + private Vector3 _graphicalInstantiatedOffsetPosition; + /// + /// GraphicalObject rotation difference from this object when this is instantiated. + /// + private Quaternion _graphicalInstantiatedOffsetRotation; + /// + /// Cached localtick for performance. + /// + private uint _localTick; + /// + /// Smoothing component for this object when not owner. + /// + private PredictedObjectSpectatorSmoother _spectatorSmoother; + /// + /// Smoothing component for this object when owner. + /// This component is also used for non-owned objects when as server. + /// + private PredictedObjectOwnerSmoother _ownerSmoother; + #endregion + + private void Awake() + { + SetInstantiatedOffsetValues(); + } + + public override void OnStartNetwork() + { + base.OnStartNetwork(); + + /* If host then initialize owner smoother. + * Host will use owner smoothing settings for more + * accurate results. */ + if (base.IsHost) + InitializeSmoother(true); + + UpdateRigidbodiesCount(true); + ConfigureRigidbodies(); + ConfigureNetworkTransform(); + base.TimeManager.OnPostTick += TimeManager_OnPostTick; + } + + public override void OnSpawnServer(NetworkConnection connection) + { + base.OnSpawnServer(connection); + Rigidbodies_OnSpawnServer(connection); + } + + public override void OnStartClient() + { + base.OnStartClient(); + ChangeSubscriptions(true); + Rigidbodies_OnStartClient(); + } + + public override void OnOwnershipClient(NetworkConnection prevOwner) + { + base.OnOwnershipClient(prevOwner); + /* If owner or host then use the + * owner smoother. The owner smoother + * is not predictive and is preferred + * for more real time graphical results. */ + if (base.IsOwner && !base.IsServer) + { + /* If has prediction methods implement for owner, + * otherwise implement for spectator. */ + InitializeSmoother(_implementsPredictionMethods); + /* Also set spectator smoothing if does not implement + * prediction methods as the spectator smoother is used + * for these scenarios. */ + if (!_implementsPredictionMethods) + SetTargetSmoothing(base.TimeManager.RoundTripTime, true); + } + //Not owner nor server, initialize spectator smoother if using rigidbodies. + else if (_predictionType != PredictionType.Other) + { + InitializeSmoother(false); + SetTargetSmoothing(base.TimeManager.RoundTripTime, true); + } + + Rigidbodies_OnOwnershipClient(prevOwner); + } + + public override void OnStopNetwork() + { + base.OnStopNetwork(); + + ChangeSubscriptions(false); + UpdateRigidbodiesCount(false); + base.TimeManager.OnPostTick -= TimeManager_OnPostTick; + } + + /// + /// Updates Rigidbodies count on the PredictionManager. + /// + /// + private void UpdateRigidbodiesCount(bool add) + { + if (_registered == add) + return; + if (_predictionType == PredictionType.Other) + return; + + NetworkManager nm = base.NetworkManager; + if (nm == null) + return; + + _registered = add; + + if (add) + { + nm.PredictionManager.AddRigidbodyCount(this); + nm.PredictionManager.OnPreServerReconcile += PredictionManager_OnPreServerReconcile; + } + else + { + nm.PredictionManager.RemoveRigidbodyCount(this); + nm.PredictionManager.OnPreServerReconcile -= PredictionManager_OnPreServerReconcile; + } + } + + /// + /// Sets instantiated offset values for the graphical object. + /// + private void SetInstantiatedOffsetValues() + { + transform.SetTransformOffsets(_graphicalObject, ref _graphicalInstantiatedOffsetPosition, ref _graphicalInstantiatedOffsetRotation); + } + + private void TimeManager_OnUpdate() + { + _spectatorSmoother?.ManualUpdate(); + _ownerSmoother?.ManualUpdate(); + } + + private void TimeManager_OnPreTick() + { + _localTick = base.TimeManager.LocalTick; + _spectatorSmoother?.OnPreTick(); + _ownerSmoother?.OnPreTick(); + } + + protected void TimeManager_OnPostTick() + { + _spectatorSmoother?.OnPostTick(); + _ownerSmoother?.OnPostTick(); + Rigidbodies_TimeManager_OnPostTick(); + } + + + /// + /// Subscribes to events needed to function. + /// + /// + private void ChangeSubscriptions(bool subscribe) + { + if (base.TimeManager == null) + return; + if (subscribe == _clientSubscribed) + return; + + if (subscribe) + { + base.TimeManager.OnUpdate += TimeManager_OnUpdate; + base.TimeManager.OnPreTick += TimeManager_OnPreTick; + //Only client will use these events. + if (!base.IsServer) + { + base.PredictionManager.OnPreReplicateReplay += PredictionManager_OnPreReplicateReplay; + base.PredictionManager.OnPostReplicateReplay += PredictionManager_OnPostReplicateReplay; + base.PredictionManager.OnPreReconcile += PredictionManager_OnPreReconcile; + base.PredictionManager.OnPostReconcile += PredictionManager_OnPostReconcile; + base.TimeManager.OnRoundTripTimeUpdated += TimeManager_OnRoundTripTimeUpdated; + } + } + else + { + base.TimeManager.OnUpdate -= TimeManager_OnUpdate; + base.TimeManager.OnPreTick -= TimeManager_OnPreTick; + //Only client will use these events. + if (!base.IsServer) + { + base.PredictionManager.OnPreReplicateReplay -= PredictionManager_OnPreReplicateReplay; + base.PredictionManager.OnPostReplicateReplay -= PredictionManager_OnPostReplicateReplay; + base.PredictionManager.OnPreReconcile -= PredictionManager_OnPreReconcile; + base.PredictionManager.OnPostReconcile -= PredictionManager_OnPostReconcile; + base.TimeManager.OnRoundTripTimeUpdated -= TimeManager_OnRoundTripTimeUpdated; + } + + //Also some resets + _lastStateLocalTick = 0; + _rigidbodyStates.Clear(); + _rigidbody2dStates.Clear(); + } + + _clientSubscribed = subscribe; + } + + private void TimeManager_OnRoundTripTimeUpdated(long obj) + { + Rigidbodies_OnRoundTripTimeUpdated(obj); + } + + private void PredictionManager_OnPreServerReconcile(NetworkBehaviour obj) + { + SendRigidbodyState(obj); + } + + /// + /// Called before physics is simulated when replaying a replicate method. + /// Contains the PhysicsScene and PhysicsScene2D which was simulated. + /// + protected virtual void PredictionManager_OnPreReplicateReplay(uint tick, PhysicsScene ps, PhysicsScene2D ps2d) + { + _spectatorSmoother?.OnPreReplay(tick); + Rigidbodies_PredictionManager_OnPreReplicateReplay(tick, ps, ps2d); + } + + /// + /// Called after physics is simulated when replaying a replicate method. + /// Contains the PhysicsScene and PhysicsScene2D which was simulated. + /// + private void PredictionManager_OnPostReplicateReplay(uint tick, PhysicsScene ps, PhysicsScene2D ps2d) + { + _spectatorSmoother?.OnPostReplay(tick); + Rigidbodies_PredictionManager_OnPostReplicateReplay(tick, ps, ps2d); + } + + /// + /// Called before performing a reconcile on NetworkBehaviour. + /// + private void PredictionManager_OnPreReconcile(NetworkBehaviour nb) + { + Rigidbodies_TimeManager_OnPreReconcile(nb); + } + + /// + /// Called after performing a reconcile on NetworkBehaviour. + /// + private void PredictionManager_OnPostReconcile(NetworkBehaviour nb) + { + Rigidbodies_TimeManager_OnPostReconcile(nb); + } + + /// + /// Initializes a smoother with configured values. + /// + private void InitializeSmoother(bool ownerSmoother) + { + ResetGraphicalTransform(); + + if (ownerSmoother) + { + _ownerSmoother = new PredictedObjectOwnerSmoother(); + float teleportThreshold = (_enableTeleport) ? _teleportThreshold : -1f; + _ownerSmoother.Initialize(this, _graphicalInstantiatedOffsetPosition, _graphicalInstantiatedOffsetRotation, _graphicalObject, _ownerSmoothPosition, _ownerSmoothRotation, _ownerInterpolation, teleportThreshold); + } + else + { + _spectatorSmoother = new PredictedObjectSpectatorSmoother(); + RigidbodyType rbType = (_predictionType == PredictionType.Rigidbody) ? + RigidbodyType.Rigidbody : RigidbodyType.Rigidbody2D; + float teleportThreshold = (_enableTeleport) ? _teleportThreshold : -1f; + _spectatorSmoother.Initialize(this, rbType, _rigidbody, _rigidbody2d, _graphicalObject, _spectatorSmoothPosition, _spectatorSmoothRotation, teleportThreshold); + } + + void ResetGraphicalTransform() + { + _graphicalObject.position = (transform.position + _graphicalInstantiatedOffsetPosition); + _graphicalObject.rotation = (_graphicalInstantiatedOffsetRotation * transform.rotation); + } + } + + /// + /// Configures RigidbodyPauser with settings. + /// + private void ConfigureRigidbodies() + { + if (!IsRigidbodyPrediction) + return; + + _rigidbodyPauser = new RigidbodyPauser(); + if (_predictionType == PredictionType.Rigidbody) + { + _rigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous; + _rigidbodyPauser.UpdateRigidbodies(transform, RigidbodyType.Rigidbody, true, _graphicalObject); + } + else + { + _rigidbody2d.collisionDetectionMode = CollisionDetectionMode2D.Continuous; + _rigidbodyPauser.UpdateRigidbodies(transform, RigidbodyType.Rigidbody2D, true, _graphicalObject); + } + } + + /// + /// Configures NetworkTransform for prediction. + /// + private void ConfigureNetworkTransform() + { + if (!IsRigidbodyPrediction) + _networkTransform?.ConfigureForCSP(); + } + + +#if UNITY_EDITOR + protected override void OnValidate() + { + if (Application.isPlaying) + { + InitializeSmoother(true); + } + else + { + if (_spectatorSmoothingType == SpectatorSmoothingType.Accuracy) + _preconfiguredSmoothingDataPreview = _accurateSmoothingData; + else if (_spectatorSmoothingType == SpectatorSmoothingType.Mixed) + _preconfiguredSmoothingDataPreview = _mixedSmoothingData; + else if (_spectatorSmoothingType == SpectatorSmoothingType.Gradual) + _preconfiguredSmoothingDataPreview = _gradualSmoothingData; + } + } +#endif + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObject.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObject.cs.meta new file mode 100644 index 0000000..82837b8 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObject.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 402926ef33e0a894d9fec352693988ac +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObjectOwnerSmoother.cs b/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObjectOwnerSmoother.cs new file mode 100644 index 0000000..dca6196 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObjectOwnerSmoother.cs @@ -0,0 +1,299 @@ +using FishNet.Utility.Extension; +using FishNet.Object; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Component.Prediction +{ + internal class PredictedObjectOwnerSmoother + { + #region Serialized. + /// + /// Transform which holds the graphical features of this object. This transform will be smoothed when desynchronizations occur. + /// + private Transform _graphicalObject; + /// + /// Sets GraphicalObject. + /// + /// + public void SetGraphicalObject(Transform value) + { + _graphicalObject = value; + _networkBehaviour.transform.SetTransformOffsets(value, ref _graphicalInstantiatedOffsetPosition, ref _graphicalInstantiatedOffsetRotation); + } + /// + /// NetworkBehaviour which is using this object. + /// + private NetworkBehaviour _networkBehaviour; + /// + /// How far the transform must travel in a single update to cause a teleport rather than smoothing. Using 0f will teleport every update. + /// + private float _teleportThreshold = 1f; + /// + /// How far in the past to keep the graphical object when owner. + /// + private byte _interpolation = 1; + /// + /// Sets the interpolation value to use when the owner of this object. + /// + /// + public void SetInterpolation(byte value) => _interpolation = value; + #endregion + + #region Private. + /// + /// World position before transform was predicted or reset. + /// + private Vector3 _graphicalStartPosition; + /// + /// World rotation before transform was predicted or reset. + /// + private Quaternion _graphicalStartRotation; + /// + /// GraphicalObject position difference from the PredictedObject when this is initialized. + /// + private Vector3 _graphicalInstantiatedOffsetPosition; + /// + /// How quickly to move towards TargetPosition. + /// + private float _positionMoveRate = -2; + /// + /// GraphicalObject rotation difference from the PredictedObject when this is initialized. + /// + private Quaternion _graphicalInstantiatedOffsetRotation; + /// + /// How quickly to move towards TargetRotation. + /// + private float _rotationMoveRate = -2; + /// + /// True if OnPreTick was received this frame. + /// + private bool _preTickReceived; + /// + /// True to move towards position goals. + /// + private bool _smoothPosition; + /// + /// True to move towards rotation goals. + /// + private bool _smoothRotation; + #endregion + + /// + /// Initializes this script for use. + /// + public void Initialize(NetworkBehaviour nb, Vector3 instantiatedOffsetPosition, Quaternion instantiatedOffsetRotation, Transform graphicalObject + , bool smoothPosition, bool smoothRotation, byte interpolation, float teleportThreshold) + { + _networkBehaviour = nb; + _graphicalInstantiatedOffsetPosition = instantiatedOffsetPosition; + _graphicalInstantiatedOffsetRotation = instantiatedOffsetRotation; + _graphicalObject = graphicalObject; + + _smoothPosition = smoothPosition; + _smoothRotation = smoothRotation; + + _interpolation = interpolation; + _teleportThreshold = teleportThreshold; + } + + /// + /// Called every frame. + /// + public void ManualUpdate() + { + MoveToTarget(); + } + + /// + /// Called when the TimeManager invokes OnPreTick. + /// + public void OnPreTick() + { + if (CanSmooth()) + { + _preTickReceived = true; + /* Only snap to destination if interpolation is 1. + * This ensures the graphics will be at the proper location + * before the next movement rates are calculated. */ + if (_interpolation == 1) + ResetGraphicalToInstantiatedProperties(true, true); + + SetGraphicalPreviousProperties(); + } + } + + public void OnPostTick() + { + if (CanSmooth() && _preTickReceived) + { + _preTickReceived = false; + ResetGraphicalToPreviousProperties(); + SetGraphicalMoveRates(); + } + } + + /// + /// Returns if prediction can be used on this rigidbody. + /// + /// + private bool CanSmooth() + { + if (_interpolation == 0) + return false; + //Only owner needs smoothing. + if (!_networkBehaviour.IsOwner && !_networkBehaviour.IsHost) + return false; + + return true; + } + + /// + /// Moves transform to target values. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void MoveToTarget() + { + //Not set, meaning movement doesnt need to happen or completed. + if (_positionMoveRate == -2f && _rotationMoveRate == -2f) + return; + + Vector3 posGoal = GetGraphicalGoalPosition(); + Quaternion rotGoal = GetGraphicalGoalRotation(); + + /* Only try to update properties if they have a valid move rate. + * Properties may have 0f move rate if they did not change. */ + Transform t = _graphicalObject; + float delta = Time.deltaTime; + + //Position. + if (SmoothPosition()) + { + if (_positionMoveRate == -1f) + ResetGraphicalToInstantiatedProperties(true, false); + else if (_positionMoveRate > 0f) + t.position = Vector3.MoveTowards(t.position, posGoal, _positionMoveRate * delta); + } + + //Rotation. + if (SmoothRotation()) + { + if (_rotationMoveRate == -1f) + ResetGraphicalToInstantiatedProperties(false, true); + else if (_rotationMoveRate > 0f) + t.rotation = Quaternion.RotateTowards(t.rotation, rotGoal, _rotationMoveRate * delta); + } + + if (GraphicalObjectMatches(posGoal, rotGoal)) + { + _positionMoveRate = -2f; + _rotationMoveRate = -2f; + } + } + + /// + /// Returns if this transform matches arguments. + /// + /// + private bool GraphicalObjectMatches(Vector3 position, Quaternion rotation) + { + bool positionMatches = (!_smoothPosition || (_graphicalObject.position == position)); + bool rotationMatches = (!_smoothRotation || (_graphicalObject.rotation == rotation)); + return (positionMatches && rotationMatches); + } + + /// + /// True to smooth position. When false the graphicalObjects property will not be updated. + /// + /// + private bool SmoothPosition() => (_smoothPosition && (_networkBehaviour.IsOwner || _networkBehaviour.IsHost)); + /// + /// True to smooth rotation. When false the graphicalObjects property will not be updated. + /// + /// + private bool SmoothRotation() => (_smoothRotation && (_networkBehaviour.IsOwner || _networkBehaviour.IsHost)); + + /// + /// Sets Position and Rotation move rates to reach Target datas. + /// + private void SetGraphicalMoveRates() + { + float delta = ((float)_networkBehaviour.TimeManager.TickDelta * _interpolation); + + float distance; + distance = Vector3.Distance(_graphicalObject.position, GetGraphicalGoalPosition()); + //If qualifies for teleporting. + if (_teleportThreshold != -1f && distance >= _teleportThreshold) + { + _positionMoveRate = -1f; + _rotationMoveRate = -1f; + } + //Smoothing. + else + { + _positionMoveRate = (distance / delta); + distance = Quaternion.Angle(_graphicalObject.rotation, GetGraphicalGoalRotation()); + if (distance > 0f) + _rotationMoveRate = (distance / delta); + } + } + + /// + /// Gets a goal position for the graphical object. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Vector3 GetGraphicalGoalPosition() + { + if (SmoothPosition()) + return (_networkBehaviour.transform.position + _graphicalInstantiatedOffsetPosition); + else + return _graphicalObject.position; + } + + /// + /// Gets a goal rotation for the graphical object. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Quaternion GetGraphicalGoalRotation() + { + if (SmoothRotation()) + return (_graphicalInstantiatedOffsetRotation * _networkBehaviour.transform.rotation); + else + return _graphicalObject.rotation; + } + /// + /// Caches the graphical object' current position and rotation. + /// + private void SetGraphicalPreviousProperties() + { + _graphicalStartPosition = _graphicalObject.position; + _graphicalStartRotation = _graphicalObject.rotation; + } + + /// + /// Resets the graphical object to cached position and rotation of the transform. + /// + private void ResetGraphicalToPreviousProperties() + { + _graphicalObject.SetPositionAndRotation(_graphicalStartPosition, _graphicalStartRotation); + } + + /// + /// Resets the graphical object to it's transform offsets during instantiation. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ResetGraphicalToInstantiatedProperties(bool position, bool rotation) + { + if (position) + _graphicalObject.position = GetGraphicalGoalPosition(); + if (rotation) + _graphicalObject.rotation = GetGraphicalGoalRotation(); + } + + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObjectOwnerSmoother.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObjectOwnerSmoother.cs.meta new file mode 100644 index 0000000..b24552a --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObjectOwnerSmoother.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8b3bc1d8919cdf749971d3d4d44b198f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObjectSpectatorSmoother.cs b/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObjectSpectatorSmoother.cs new file mode 100644 index 0000000..cbe0ab7 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObjectSpectatorSmoother.cs @@ -0,0 +1,884 @@ +using FishNet.Object; +using FishNet.Transporting; +using FishNet.Utility.Extension; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + + +namespace FishNet.Component.Prediction +{ + internal class PredictedObjectSpectatorSmoother + { + #region Types. + /// + /// Data on a goal to move towards. + /// + private class GoalData + { + public bool IsActive; + /// + /// LocalTick this data is for. + /// + public uint LocalTick; + /// + /// Data on how fast to move to transform values. + /// + public RateData Rates = new RateData(); + /// + /// Transform values to move towards. + /// + public TransformData Transforms = new TransformData(); + + public GoalData() { } + /// + /// Resets values for re-use. + /// + public void Reset() + { + LocalTick = 0; + Transforms.Reset(); + Rates.Reset(); + IsActive = false; + } + + /// + /// Updates values using a GoalData. + /// + public void Update(GoalData gd) + { + LocalTick = gd.LocalTick; + Rates.Update(gd.Rates); + Transforms.Update(gd.Transforms); + IsActive = true; + } + + public void Update(uint localTick, RateData rd, TransformData td) + { + LocalTick = localTick; + Rates = rd; + Transforms = td; + IsActive = true; + } + } + /// + /// How fast to move to values. + /// + private class RateData + { + /// + /// Rate for position after smart calculations. + /// + public float Position; + /// + /// Rate for rotation after smart calculations. + /// + public float Rotation; + /// + /// Number of ticks the rates are calculated for. + /// If TickSpan is 2 then the rates are calculated under the assumption the transform changed over 2 ticks. + /// + public uint TickSpan; + /// + /// Time remaining until transform is expected to reach it's goal. + /// + internal float TimeRemaining; + + public RateData() { } + + /// + /// Resets values for re-use. + /// + public void Reset() + { + Position = 0f; + Rotation = 0f; + TickSpan = 0; + TimeRemaining = 0f; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Update(RateData rd) + { + Update(rd.Position, rd.Rotation, rd.TickSpan, rd.TimeRemaining); + } + + /// + /// Updates rates. + /// + public void Update(float position, float rotation, uint tickSpan, float timeRemaining) + { + Position = position; + Rotation = rotation; + TickSpan = tickSpan; + TimeRemaining = timeRemaining; + } + } + /// + /// Data about where a transform should move towards. + /// + private class TransformData + { + /// + /// Position of the transform. + /// + public Vector3 Position; + /// + /// Rotation of the transform. + /// + public Quaternion Rotation; + + public void Reset() + { + Position = Vector3.zero; + Rotation = Quaternion.identity; + } + /// + /// Updates this data. + /// + public void Update(TransformData copy) + { + Update(copy.Position, copy.Rotation); + } + /// + /// Updates this data. + /// + public void Update(Vector3 position, Quaternion rotation) + { + Position = position; + Rotation = rotation; + } + /// + /// Updates this data. + /// + public void Update(Rigidbody rigidbody) + { + Position = rigidbody.transform.position; + Rotation = rigidbody.transform.rotation; + } + /// + /// Updates this data. + /// + public void Update(Rigidbody2D rigidbody) + { + Position = rigidbody.transform.position; + Rotation = rigidbody.transform.rotation; + } + } + #endregion + + #region Private. + /// + /// Current GoalData being used. + /// + private GoalData _currentGoalData = new GoalData(); + /// + /// Object to smooth. + /// + private Transform _graphicalObject; + /// + /// Sets GraphicalObject. + /// + /// + public void SetGraphicalObject(Transform value) => _graphicalObject = value; + /// + /// True to move towards position goals. + /// + private bool _smoothPosition; + /// + /// True to move towards rotation goals. + /// + private bool _smoothRotation; + /// + /// How far in the past to keep the graphical object. + /// + private uint _interpolation = 4; + /// + /// Sets the interpolation value to use when the owner of this object. + /// + /// + public void SetInterpolation(uint value) => _interpolation = value; + /// + /// GoalDatas to move towards. + /// + private List _goalDatas = new List(); + /// + /// Rigidbody to use. + /// + private Rigidbody _rigidbody; + /// + /// Rigidbody2D to use. + /// + private Rigidbody2D _rigidbody2d; + /// + /// Transform state during PreTick. + /// + private TransformData _preTickTransformdata = new TransformData(); + /// + /// Type of rigidbody being used. + /// + private RigidbodyType _rigidbodyType; + /// + /// Last tick which a reconcile occured. This is reset at the end of a tick. + /// + private long _reconcileLocalTick = -1; + /// + /// Called when this frame receives OnPreTick. + /// + private bool _preTickReceived; + /// + /// Start position for graphicalObject at the beginning of the tick. + /// + private Vector3 _graphicalStartPosition; + /// + /// Start rotation for graphicalObject at the beginning of the tick. + /// + private Quaternion _graphicalStartRotation; + /// + /// How far a distance change must exceed to teleport the graphical object. -1f indicates teleport is not enabled. + /// + private float _teleportThreshold; + /// + /// PredictedObject which is using this object. + /// + private PredictedObject _predictedObject; + /// + /// Cache of GoalDatas to prevent allocations. + /// + private static Stack _goalDataCache = new Stack(); + /// + /// Cached localtick for performance. + /// + private uint _localTick; + /// + /// Number of ticks to ignore when replaying. + /// + private uint _ignoredTicks; + /// + /// Start position of the graphical object in world space. + /// + private Vector3 _startWorldPosition; + #endregion + + #region Const. + /// + /// Multiplier to apply to movement speed when buffer is over interpolation. + /// + private const float OVERFLOW_MULTIPLIER = 0.1f; + /// + /// Multiplier to apply to movement speed when buffer is under interpolation. + /// + private const float UNDERFLOW_MULTIPLIER = 0.02f; + #endregion + + public void SetIgnoredTicks(uint value) => _ignoredTicks = value; + /// + /// Initializes this for use. + /// + internal void Initialize(PredictedObject po, RigidbodyType rbType, Rigidbody rb, Rigidbody2D rb2d, Transform graphicalObject + , bool smoothPosition, bool smoothRotation, float teleportThreshold) + { + _predictedObject = po; + _rigidbodyType = rbType; + + _rigidbody = rb; + _rigidbody2d = rb2d; + _graphicalObject = graphicalObject; + _startWorldPosition = _graphicalObject.position; + _smoothPosition = smoothPosition; + _smoothRotation = smoothRotation; + _teleportThreshold = teleportThreshold; + } + + /// + /// + /// Called every frame. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ManualUpdate() + { + if (CanSmooth()) + MoveToTarget(); + } + + /// + /// Called when the TimeManager invokes OnPreTick. + /// + public void OnPreTick() + { + if (CanSmooth()) + { + _localTick = _predictedObject.TimeManager.LocalTick; + if (!_preTickReceived) + { + uint tick = _predictedObject.TimeManager.LocalTick - 1; + CreateGoalData(tick, false); + } + _preTickReceived = true; + + if (_rigidbodyType == RigidbodyType.Rigidbody) + _preTickTransformdata.Update(_rigidbody); + else + _preTickTransformdata.Update(_rigidbody2d); + + _graphicalStartPosition = _graphicalObject.position; + _graphicalStartRotation = _graphicalObject.rotation; + } + } + + /// + /// Called when the TimeManager invokes OnPostTick. + /// + public void OnPostTick() + { + if (CanSmooth()) + { + if (!_preTickReceived) + { + /* During test the Z value for applyImmediately is 5.9. + * Then increased 1 unit per tick: 6.9, 7.9. + * + * When the spectator smoother initializes 5.9 is shown. + * Before first starting smoothing the transform needs to be set + * back to that. + * + * The second issue is the first addition to goal datas seems + * to occur at 7.9. This would need to be 6.9 to move from the + * proper 5.9 starting point. It's probably because pretick is not received + * when OnPostTick is called at the 6.9 position. + * + * Have not validated the above yet but that's the most likely situation since + * we know this was initialized at 5.9, which means it would be assumed pretick would + * call at 6.9. Perhaps the following is happening.... + * + * - Pretick. + * - Client gets spawn+applyImmediately. + * - This also initializes this script at 5.9. + * - Simulation moves object to 6.9. + * - PostTick. + * - This script does not run because _preTickReceived is not set yet. + * + * - Pretick. Sets _preTickReceived. + * - Simulation moves object to 7.9. + * - PostTick. + * - The first goalData is created for 7.9. + * + * In writing the theory checks out. + * Perhaps the solution could be simple as creating a goal + * during pretick if _preTickReceived is being set for + * the first time. Might need to reduce tick by 1 + * when setting goalData for this; not sure yet. + */ + _graphicalObject.SetPositionAndRotation(_startWorldPosition, Quaternion.identity); + return; + } + + _graphicalObject.SetPositionAndRotation(_graphicalStartPosition, _graphicalStartRotation); + CreateGoalData(_predictedObject.TimeManager.LocalTick, true); + } + } + + public void OnPreReplay(uint tick) + { + if (!_preTickReceived) + { + if (CanSmooth()) + { + //if (_localTick - tick < _ignoredTicks) + // return; + + CreateGoalData(tick, false); + } + } + } + + /// + /// Called after a reconcile runs a replay. + /// + public void OnPostReplay(uint tick) + { + if (CanSmooth()) + { + if (_reconcileLocalTick == -1) + return; + + CreateGoalData(tick, false); + } + } + + /// + /// Returns if the graphics can be smoothed. + /// + /// + private bool CanSmooth() + { + if (_interpolation == 0) + return false; + if (_predictedObject.IsPredictingOwner() || _predictedObject.IsServer) + return false; + + return true; + } + + + /// + /// Sets the last tick a reconcile occurred. + /// + /// + public void SetLocalReconcileTick(long value) + { + _reconcileLocalTick = value; + } + + /// + /// Caches a GoalData. + /// + /// + private void StoreGoalData(GoalData gd) + { + gd.Reset(); + _goalDataCache.Push(gd); + } + + /// + /// Returns if this transform matches arguments. + /// + /// + private bool GraphicalObjectMatches(Vector3 localPosition, Quaternion localRotation) + { + bool positionMatches = (!_smoothPosition || _graphicalObject.position == localPosition); + bool rotationMatches = (!_smoothRotation || _graphicalObject.rotation == localRotation); + return (positionMatches && rotationMatches); + } + + /// + /// Returns if there is any change between two datas. + /// + private bool HasChanged(TransformData a, TransformData b) + { + return (a.Position != b.Position) || + (a.Rotation != b.Rotation); + } + + /// + /// Returns if the transform differs from td. + /// + private bool HasChanged(TransformData td) + { + Transform rigidbodyTransform; + + if (_rigidbodyType == RigidbodyType.Rigidbody) + rigidbodyTransform = _rigidbody.transform; + else + rigidbodyTransform = _rigidbody2d.transform; + + bool changed = (td.Position != rigidbodyTransform.position) || (td.Rotation != rigidbodyTransform.rotation); + + return changed; + } + + /// + /// Sets CurrentGoalData to the next in queue. + /// + private void SetCurrentGoalData(bool afterMove) + { + if (_goalDatas.Count == 0) + { + _currentGoalData.IsActive = false; + } + else + { + //if (!afterMove && _goalDatas.Count < _interpolation) + // return; + + //Update current to next. + _currentGoalData.Update(_goalDatas[0]); + //Store old and remove it. + StoreGoalData(_goalDatas[0]); + _goalDatas.RemoveAt(0); + } + } + + /// + /// Moves to a GoalData. Automatically determins if to use data from server or client. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void MoveToTarget(float deltaOverride = -1f) + { + /* If the current goal data is not active then + * try to set a new one. If none are available + * it will remain inactive. */ + if (!_currentGoalData.IsActive) + { + SetCurrentGoalData(false); + //If still inactive then it could not be updated. + if (!_currentGoalData.IsActive) + return; + } + + float delta = (deltaOverride != -1f) ? deltaOverride : Time.deltaTime; + /* Once here it's safe to assume the object will be moving. + * Any checks which would stop it from moving be it client + * auth and owner, or server controlled and server, ect, + * would have already been run. */ + TransformData td = _currentGoalData.Transforms; + RateData rd = _currentGoalData.Rates; + + int queueCount = _goalDatas.Count; + /* Begin moving even if interpolation buffer isn't + * met to provide more real-time interactions but + * speed up when buffer is too large. This should + * provide a good balance of accuracy. */ + + float multiplier; + int countOverInterpolation = (queueCount - (int)_interpolation); + if (countOverInterpolation > 0) + { + float overflowMultiplier = (!_predictedObject.IsOwner) ? OVERFLOW_MULTIPLIER : (OVERFLOW_MULTIPLIER * 1f); + multiplier = 1f + overflowMultiplier; + } + else if (countOverInterpolation < 0) + { + float value = (UNDERFLOW_MULTIPLIER * Mathf.Abs(countOverInterpolation)); + const float maximum = 0.9f; + if (value > maximum) + value = maximum; + multiplier = 1f - value; + } + else + { + multiplier = 1f; + } + + //Rate to update. Changes per property. + float rate; + Transform t = _graphicalObject; + + //Position. + if (_smoothPosition) + { + rate = rd.Position; + Vector3 posGoal = td.Position; + if (rate == -1f) + t.position = td.Position; + else if (rate > 0f) + t.position = Vector3.MoveTowards(t.position, posGoal, rate * delta * multiplier); + } + + //Rotation. + if (_smoothRotation) + { + rate = rd.Rotation; + if (rate == -1f) + t.rotation = td.Rotation; + else if (rate > 0f) + t.rotation = Quaternion.RotateTowards(t.rotation, td.Rotation, rate * delta); + } + + //Subtract time remaining for movement to complete. + if (rd.TimeRemaining > 0f) + { + float subtractionAmount = (delta * multiplier); + float timeRemaining = rd.TimeRemaining - subtractionAmount; + rd.TimeRemaining = timeRemaining; + } + + //If movement shoudl be complete. + if (rd.TimeRemaining <= 0f) + { + float leftOver = Mathf.Abs(rd.TimeRemaining); + //Set to next goal data if available. + SetCurrentGoalData(true); + + //New data was set. + if (_currentGoalData.IsActive) + { + if (leftOver > 0f) + MoveToTarget(leftOver); + } + //No more in buffer, see if can extrapolate. + else + { + /* Everything should line up when + * time remaining is <= 0f but incase it's not, + * such as if the user manipulated the grapihc object + * somehow, then set goaldata active again to continue + * moving it until it lines up with the goal. */ + if (!GraphicalObjectMatches(td.Position, td.Rotation)) + _currentGoalData.IsActive = true; + } + } + } + + #region Rates. + /// + /// Sets move rates which will occur instantly. + /// + private void SetInstantRates(RateData rd) + { + rd.Update(-1f, -1f, 1, -1f); + } + + /// + /// Sets move rates which will occur over time. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SetCalculatedRates(GoalData prevGoalData, GoalData nextGoalData, Channel channel) + { + /* Only update rates if data has changed. + * When data comes in reliably for eventual consistency + * it's possible that it will be the same as the last + * unreliable packet. When this happens no change has occurred + * and the distance of change woudl also be 0; this prevents + * the NT from moving. Only need to compare data if channel is reliable. */ + TransformData nextTd = nextGoalData.Transforms; + if (channel == Channel.Reliable && HasChanged(prevGoalData.Transforms, nextTd)) + { + nextGoalData.Rates.Update(prevGoalData.Rates); + return; + } + + uint lastTick = prevGoalData.LocalTick; + /* How much time has passed between last update and current. + * If set to 0 then that means the transform has + * settled. */ + if (lastTick == 0) + lastTick = (nextGoalData.LocalTick - 1); + + uint tickDifference = (nextGoalData.LocalTick - lastTick); + float timePassed = (float)_predictedObject.TimeManager.TicksToTime(tickDifference); + RateData nextRd = nextGoalData.Rates; + + //Distance between properties. + float distance; + //Position. + Vector3 lastPosition = prevGoalData.Transforms.Position; + distance = Vector3.Distance(lastPosition, nextTd.Position); + //If distance teleports assume rest do. + if (_teleportThreshold >= 0f && distance >= _teleportThreshold) + { + SetInstantRates(nextRd); + return; + } + + //Position distance already calculated. + float positionRate = (distance / timePassed); + //Rotation. + distance = prevGoalData.Transforms.Rotation.Angle(nextTd.Rotation, true); + float rotationRate = (distance / timePassed); + + /* If no speed then snap just in case. + * 0f could be from floating errors. */ + if (positionRate == 0f) + positionRate = -1f; + if (rotationRate == 0f) + rotationRate = -1f; + + nextRd.Update(positionRate, rotationRate, tickDifference, timePassed); + } + #endregion + + /// + /// Creates a new goal data for tick. The result will be placed into the goalDatas queue at it's proper position. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CreateGoalData(uint tick, bool postTick) + { + /* It's possible a removed entry would void further + * logic so remove excess entires first. */ + + /* Remove entries which are excessive to the buffer. + * This could create a starting jitter but it will ensure + * the buffer does not fill too much. The buffer next should + * actually get unreasonably high but rather safe than sorry. */ + int maximumBufferAllowance = ((int)_interpolation * 8); + int removedBufferCount = (_goalDatas.Count - maximumBufferAllowance); + //If there are some to remove. + if (removedBufferCount > 0) + { + for (int i = 0; i < removedBufferCount; i++) + StoreGoalData(_goalDatas[0 + i]); + _goalDatas.RemoveRange(0, removedBufferCount); + } + + uint currentGoalDataTick = _currentGoalData.LocalTick; + //Tick has already been interpolated past, no reason to process it. + if (tick <= currentGoalDataTick) + return; + + //GoalData from previous calculation. + GoalData prevGoalData; + int datasCount = _goalDatas.Count; + /* Where to insert next data. This could have value + * somewhere in the middle of goalDatas if the tick + * is a replay rather than post tick. */ + int injectionIndex = datasCount + 1; + //If being added at the end of a tick rather than from replay. + if (postTick) + { + //Becomes true if transform differs from previous data. + bool changed; + + //If there is no goal data then create one using pretick data. + if (datasCount == 0) + { + prevGoalData = MakeGoalDataFromPreTickTransform(); + changed = HasChanged(prevGoalData.Transforms); + } + //If there's goal datas grab the last, it will always be the tick before. + else + { + prevGoalData = _goalDatas[datasCount - 1]; + /* If the tick is not exactly 1 past the last + * then there's gaps in the saved values. This can + * occur if the transform went idle and the buffer + * hasn't emptied out yet. When this occurs use the + * preTick data to calculate differences. */ + if (tick - prevGoalData.LocalTick != 1) + prevGoalData = MakeGoalDataFromPreTickTransform(); + + changed = HasChanged(prevGoalData.Transforms); + } + + //Nothing has changed so no further action is required. + if (!changed) + { + if (datasCount > 0 && prevGoalData != _goalDatas[datasCount - 1]) + StoreGoalData(prevGoalData); + return; + } + } + //Not post tick so it's from a replay. + else + { + int prevIndex = -1; + /* If the tick is 1 past current goalData + * then it's the next in line for smoothing + * from the current. + * When this occurs use currentGoalData as + * the previous. */ + if (tick == (currentGoalDataTick + 1)) + { + prevGoalData = _currentGoalData; + injectionIndex = 0; + } + //When not the next in line find out where to place data. + else + { + if (tick > 0) + prevGoalData = GetGoalData(tick - 1, out prevIndex); + //Cannot find prevGoalData if tick is 0. + else + prevGoalData = null; + } + + //If previous goalData was found then inject just past the previous value. + if (prevIndex != -1) + injectionIndex = prevIndex + 1; + + /* Should previous goalData be null then it could not be found. + * Create a new previous goal data based on rigidbody state + * during pretick. */ + if (prevGoalData == null) + { + //Create a goaldata based on information. If it differs from pretick then throw. + GoalData gd = RetrieveGoalData(); + gd.Transforms.Update(_preTickTransformdata); + + if (HasChanged(gd.Transforms)) + { + prevGoalData = gd; + } + else + { + StoreGoalData(gd); + return; + } + } + /* Previous goal data is not active. + * This should not be possible but this + * is here as a sanity check anyway. */ + else if (!prevGoalData.IsActive) + { + return; + } + } + + //Begin building next goal data. + GoalData nextGoalData = RetrieveGoalData(); + nextGoalData.LocalTick = tick; + //Set next transform data. + TransformData nextTd = nextGoalData.Transforms; + if (_rigidbodyType == RigidbodyType.Rigidbody) + nextTd.Update(_rigidbody); + else + nextTd.Update(_rigidbody2d); + + /* Reset properties if smoothing is not enabled + * for them. It's less checks and easier to do it + * after the nextGoalData is populated. */ + if (!_smoothPosition) + nextTd.Position = _graphicalStartPosition; + if (!_smoothRotation) + nextTd.Rotation = _graphicalStartRotation; + + //Calculate rates for prev vs next data. + SetCalculatedRates(prevGoalData, nextGoalData, Channel.Unreliable); + /* If injectionIndex would place at the end + * then add. to goalDatas. */ + if (injectionIndex >= _goalDatas.Count) + _goalDatas.Add(nextGoalData); + //Otherwise insert into the proper location. + else + _goalDatas[injectionIndex].Update(nextGoalData); + + //Makes previous goal data from transforms pretick values. + GoalData MakeGoalDataFromPreTickTransform() + { + GoalData gd = RetrieveGoalData(); + //RigidbodyData contains the data from preTick. + gd.Transforms.Update(_preTickTransformdata); + //No need to update rates because this is just a starting point reference for interpolation. + return gd; + } + } + + /// + /// Returns the GoalData at tick. + /// + /// + private GoalData GetGoalData(uint tick, out int index) + { + index = -1; + if (tick == 0) + return null; + + for (int i = 0; i < _goalDatas.Count; i++) + { + if (_goalDatas[i].LocalTick == tick) + { + index = i; + return _goalDatas[i]; + } + } + + //Not found. + return null; + } + + + /// + /// Returns a GoalData from the cache. + /// + /// + private GoalData RetrieveGoalData() + { + GoalData result = (_goalDataCache.Count > 0) ? _goalDataCache.Pop() : new GoalData(); + result.IsActive = true; + return result; + } + + } + + +} diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObjectSpectatorSmoother.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObjectSpectatorSmoother.cs.meta new file mode 100644 index 0000000..63c200f --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObjectSpectatorSmoother.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6bb311b5dc9602a4f8e4a8c6e64b21cb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyPauser.cs b/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyPauser.cs new file mode 100644 index 0000000..55098f4 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyPauser.cs @@ -0,0 +1,361 @@ +using FishNet.Managing; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace FishNet.Component.Prediction +{ + /// + /// Pauses and unpauses rigidbodies. While paused rigidbodies cannot be interacted with or simulated. + /// + public class RigidbodyPauser + { + #region Types. + /// + /// Data for a rigidbody before being set kinematic. + /// + private struct RigidbodyData + { + /// + /// Rigidbody for data. + /// + public Rigidbody Rigidbody; + /// + /// Cached velocity when being set kinematic. + /// + public Vector3 Velocity; + /// + /// Cached velocity when being set kinematic. + /// + public Vector3 AngularVelocity; + /// + /// Scene of this rigidbody when being set kinematic. + /// + public Scene SimulatedScene; + /// + /// True if the rigidbody was kinematic prior to being paused. + /// + public bool IsKinematic; + + public RigidbodyData(Rigidbody rb) + { + Rigidbody = rb; + Rigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous; + Velocity = Vector3.zero; + AngularVelocity = Vector3.zero; + SimulatedScene = rb.gameObject.scene; + IsKinematic = rb.isKinematic; + } + + public void Update(Rigidbody rb) + { + Velocity = rb.velocity; + AngularVelocity = rb.angularVelocity; + SimulatedScene = rb.gameObject.scene; + IsKinematic = rb.isKinematic; + } + } + /// + /// Data for a rigidbody2d before being set kinematic. + /// + private struct Rigidbody2DData + { + /// + /// Rigidbody for data. + /// + public Rigidbody2D Rigidbody2d; + /// + /// Cached velocity when being set kinematic. + /// + public Vector2 Velocity; + /// + /// Cached velocity when being set kinematic. + /// + public float AngularVelocity; + /// + /// Scene of this rigidbody when being set kinematic. + /// + public Scene SimulatedScene; + /// + /// True if the rigidbody was simulated prior to being paused. + /// + public bool Simulated; + + public Rigidbody2DData(Rigidbody2D rb) + { + Rigidbody2d = rb; + Rigidbody2d.collisionDetectionMode = CollisionDetectionMode2D.Continuous; + Velocity = Vector2.zero; + AngularVelocity = 0f; + SimulatedScene = rb.gameObject.scene; + Simulated = rb.simulated; + } + + public void Update(Rigidbody2D rb) + { + Velocity = rb.velocity; + AngularVelocity = rb.angularVelocity; + SimulatedScene = rb.gameObject.scene; + Simulated = rb.simulated; + } + } + #endregion + + #region Public. + /// + /// True if the rigidbodies are considered paused. + /// + public bool Paused { get; private set; } + #endregion + + #region Private. + /// + /// Rigidbody datas for found rigidbodies. + /// + private List _rigidbodyDatas = new List(); + /// + /// Rigidbody2D datas for found rigidbodies; + /// + private List _rigidbody2dDatas = new List(); + /// + /// Type of prediction movement which is being used. + /// + private RigidbodyType _rigidbodyType; + /// + /// + /// + private static Scene _kinematicSceneCache; + /// + /// Scene used to simulate kinematic rigidbodies. + /// + private static Scene _kinematicScene + { + get + { + if (!_kinematicSceneCache.IsValid()) + _kinematicSceneCache = SceneManager.CreateScene("RigidbodyPauser_Kinematic", new CreateSceneParameters(LocalPhysicsMode.Physics2D | LocalPhysicsMode.Physics3D)); + return _kinematicSceneCache; + } + } + /// + /// Parent of GraphicalObject prior to unparenting. + /// + private Transform _graphicalParent; + /// + /// GraphicalObject to unparent when pausing. + /// + private Transform _graphicalObject; + #endregion + + /// + /// Assigns rigidbodies. + /// + /// Rigidbodies2D to use. + public void UpdateRigidbodies(Transform t, RigidbodyType rbType, bool getInChildren, Transform graphicalObject) + { + _rigidbodyType = rbType; + _rigidbodyDatas.Clear(); + _rigidbody2dDatas.Clear(); + + //3D. + if (rbType == RigidbodyType.Rigidbody) + { + if (getInChildren) + { + Rigidbody[] rbs = t.GetComponentsInChildren(); + for (int i = 0; i < rbs.Length; i++) + _rigidbodyDatas.Add(new RigidbodyData(rbs[i])); + } + else + { + Rigidbody rb = t.GetComponent(); + if (rb != null) + _rigidbodyDatas.Add(new RigidbodyData(rb)); + } + + //Make sure all added datas are not the graphical object. + for (int i = 0; i < _rigidbodyDatas.Count; i++) + { + if (_rigidbodyDatas[i].Rigidbody.transform == graphicalObject) + { + NetworkManager.StaticLogError($"GameObject {t.name} has it's GraphicalObject as a child or on the same object as a Rigidbody object. The GraphicalObject must be a child of root, and not sit beneath or on any rigidbodies."); + graphicalObject = null; + } + } + } + //2D. + else + { + if (getInChildren) + { + Rigidbody2D[] rbs = t.GetComponentsInChildren(); + for (int i = 0; i < rbs.Length; i++) + _rigidbody2dDatas.Add(new Rigidbody2DData(rbs[i])); + } + else + { + Rigidbody2D rb = t.GetComponent(); + if (rb != null) + _rigidbody2dDatas.Add(new Rigidbody2DData(rb)); + } + + //Make sure all added datas are not the graphical object. + for (int i = 0; i < _rigidbody2dDatas.Count; i++) + { + if (_rigidbody2dDatas[i].Rigidbody2d.transform == graphicalObject) + { + NetworkManager.StaticLogError($"GameObject {t.name} has it's GraphicalObject as a child or on the same object as a Rigidbody object. The GraphicalObject must be a child of root, and not sit beneath or on any rigidbodies."); + graphicalObject = null; + } + } + } + + if (graphicalObject != null) + { + _graphicalObject = graphicalObject; + _graphicalParent = graphicalObject.parent; + } + } + + /// + /// Unpauses rigidbodies allowing them to interact normally. + /// + public void Unpause() + { + if (!Paused) + return; + Paused = false; + + //3D. + if (_rigidbodyType == RigidbodyType.Rigidbody) + { + for (int i = 0; i < _rigidbodyDatas.Count; i++) + { + if (!UnpauseRigidbody(i)) + { + _rigidbodyDatas.RemoveAt(i); + i--; + } + } + + //Sets isKinematic status and returns if successful. + bool UnpauseRigidbody(int index) + { + RigidbodyData rbData = _rigidbodyDatas[index]; + Rigidbody rb = rbData.Rigidbody; + if (rb == null) + return false; + + rb.velocity = rbData.Velocity; + rb.angularVelocity = rbData.AngularVelocity; + rb.isKinematic = rbData.IsKinematic; + SceneManager.MoveGameObjectToScene(rb.transform.root.gameObject, rbData.SimulatedScene); + return true; + } + } + //2D. + else + { + for (int i = 0; i < _rigidbody2dDatas.Count; i++) + { + if (!UnpauseRigidbody(i)) + { + _rigidbody2dDatas.RemoveAt(i); + i--; + } + } + + //Sets isKinematic status and returns if successful. + bool UnpauseRigidbody(int index) + { + Rigidbody2DData rbData = _rigidbody2dDatas[index]; + Rigidbody2D rb = rbData.Rigidbody2d; + if (rb == null) + return false; + + rb.velocity = rbData.Velocity; + rb.angularVelocity = rbData.AngularVelocity; + rb.simulated = rbData.Simulated; + rb.isKinematic = !rbData.Simulated; + SceneManager.MoveGameObjectToScene(rb.transform.root.gameObject, rbData.SimulatedScene); + return true; + } + } + + //Parent went null, then graphicalObject needs to be destroyed. + if (_graphicalParent == null && _graphicalObject != null) + MonoBehaviour.Destroy(_graphicalObject.gameObject); + else + _graphicalObject?.SetParent(_graphicalParent); + + } + + /// + /// Pauses rigidbodies preventing them from interacting. + /// + public void Pause() + { + if (Paused) + return; + Paused = true; + + _graphicalObject?.SetParent(null); + Scene kinematicScene = _kinematicScene; + + //3D. + if (_rigidbodyType == RigidbodyType.Rigidbody) + { + for (int i = 0; i < _rigidbodyDatas.Count; i++) + { + if (!PauseRigidbody(i)) + { + _rigidbodyDatas.RemoveAt(i); + i--; + } + } + + //Sets isKinematic status and returns if successful. + bool PauseRigidbody(int index) + { + RigidbodyData rbData = _rigidbodyDatas[index]; + Rigidbody rb = rbData.Rigidbody; + if (rb == null) + return false; + + rbData.Update(rb); + _rigidbodyDatas[index] = rbData; + SceneManager.MoveGameObjectToScene(rb.transform.root.gameObject, kinematicScene); + return true; + } + } + //2D. + else + { + for (int i = 0; i < _rigidbody2dDatas.Count; i++) + { + if (!PauseRigidbody(i)) + { + _rigidbody2dDatas.RemoveAt(i); + i--; + } + } + + //Sets isKinematic status and returns if successful. + bool PauseRigidbody(int index) + { + Rigidbody2DData rbData = _rigidbody2dDatas[index]; + Rigidbody2D rb = rbData.Rigidbody2d; + if (rb == null) + return false; + + rbData.Update(rb); + _rigidbody2dDatas[index] = rbData; + SceneManager.MoveGameObjectToScene(rb.transform.root.gameObject, kinematicScene); + return true; + } + } + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyPauser.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyPauser.cs.meta new file mode 100644 index 0000000..4b7e11a --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyPauser.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9536377524ca5db43aae431f983ab21f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyState.cs b/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyState.cs new file mode 100644 index 0000000..f61286f --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyState.cs @@ -0,0 +1,140 @@ +using FishNet.Component.Prediction; +using FishNet.Serializing; +using UnityEngine; + +namespace FishNet.Component.Prediction +{ + public struct RigidbodyState + { + public uint LocalTick; + public Vector3 Position; + public Quaternion Rotation; + public bool IsKinematic; + public Vector3 Velocity; + public Vector3 AngularVelocity; + + public RigidbodyState(Rigidbody rb, bool isKinematic, uint tick) : this(rb, tick) + { + Position = rb.transform.position; + Rotation = rb.transform.rotation; + IsKinematic = isKinematic; + Velocity = rb.velocity; + AngularVelocity = rb.angularVelocity; + LocalTick = tick; + + } + + public RigidbodyState(Rigidbody rb, uint tick) + { + Position = rb.transform.position; + Rotation = rb.transform.rotation; + IsKinematic = rb.isKinematic; + Velocity = rb.velocity; + AngularVelocity = rb.angularVelocity; + LocalTick = tick; + } + } + + public struct Rigidbody2DState + { + public uint LocalTick; + public Vector3 Position; + public Quaternion Rotation; + public bool Simulated; + public Vector2 Velocity; + public float AngularVelocity; + + public Rigidbody2DState(Rigidbody2D rb, bool simulated, uint tick) + { + Simulated = simulated; + Position = rb.transform.position; + Rotation = rb.transform.rotation; + Velocity = rb.velocity; + AngularVelocity = rb.angularVelocity; + LocalTick = tick; + } + + public Rigidbody2DState(Rigidbody2D rb, uint tick) + { + Simulated = rb.simulated; + Position = rb.transform.position; + Rotation = rb.transform.rotation; + Velocity = rb.velocity; + AngularVelocity = rb.angularVelocity; + LocalTick = tick; + } + } +} + +public static class RigidbodyStateSerializers +{ + + public static void WriteRigidbodyState(this Writer writer, RigidbodyState value) + { + writer.WriteUInt32(value.LocalTick, AutoPackType.Unpacked); + writer.WriteVector3(value.Position); + writer.WriteQuaternion(value.Rotation); + writer.WriteBoolean(value.IsKinematic); + if (!value.IsKinematic) + { + writer.WriteVector3(value.Velocity); + writer.WriteVector3(value.AngularVelocity); + } + } + + public static RigidbodyState ReadRigidbodyState(this Reader reader) + { + RigidbodyState state = new RigidbodyState() + { + LocalTick = reader.ReadUInt32(AutoPackType.Unpacked), + Position = reader.ReadVector3(), + Rotation = reader.ReadQuaternion(), + IsKinematic = reader.ReadBoolean() + }; + + if (!state.IsKinematic) + { + state.Velocity = reader.ReadVector3(); + state.AngularVelocity = reader.ReadVector3(); + } + + return state; + } + + + public static void WriteRigidbody2DState(this Writer writer, Rigidbody2DState value) + { + writer.WriteUInt32(value.LocalTick, AutoPackType.Unpacked); + writer.WriteVector3(value.Position); + writer.WriteQuaternion(value.Rotation); + writer.WriteBoolean(value.Simulated); + + if (value.Simulated) + { + writer.WriteVector2(value.Velocity); + writer.WriteSingle(value.AngularVelocity); + } + } + + public static Rigidbody2DState ReadRigidbody2DState(this Reader reader) + { + Rigidbody2DState state = new Rigidbody2DState() + { + LocalTick = reader.ReadUInt32(AutoPackType.Unpacked), + Position = reader.ReadVector3(), + Rotation = reader.ReadQuaternion(), + Simulated = reader.ReadBoolean(), + }; + + if (state.Simulated) + { + state.Velocity = reader.ReadVector2(); + state.AngularVelocity = reader.ReadSingle(); + } + + return state; + } + + +} + diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyState.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyState.cs.meta new file mode 100644 index 0000000..b3f5928 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 05dbb585c2bc6bf4dbbc592bea73d2fe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyType.cs b/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyType.cs new file mode 100644 index 0000000..a265b55 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyType.cs @@ -0,0 +1,12 @@ +namespace FishNet.Component.Prediction +{ + /// + /// Type of prediction movement being used. + /// + public enum RigidbodyType : byte + { + Rigidbody = 0, + Rigidbody2D = 1 + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyType.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyType.cs.meta new file mode 100644 index 0000000..95cfe46 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7cf2d3fc2ff7a9042b4b7618db15b482 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/Spawning.meta b/Assets/FishNet/Runtime/Generated/Component/Spawning.meta new file mode 100644 index 0000000..1e253fc --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Spawning.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c4ff15ab60e92f14499393c8b415ea2f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/Spawning/PlayerSpawner.cs b/Assets/FishNet/Runtime/Generated/Component/Spawning/PlayerSpawner.cs new file mode 100644 index 0000000..f5cefff --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Spawning/PlayerSpawner.cs @@ -0,0 +1,159 @@ +using FishNet.Connection; +using FishNet.Managing; +using FishNet.Object; +using System; +using UnityEngine; +using UnityEngine.Serialization; + +namespace FishNet.Component.Spawning +{ + + /// + /// Spawns a player object for clients when they connect. + /// Must be placed on or beneath the NetworkManager object. + /// + [AddComponentMenu("FishNet/Component/PlayerSpawner")] + public class PlayerSpawner : MonoBehaviour + { + #region Public. + /// + /// Called on the server when a player is spawned. + /// + public event Action OnSpawned; + #endregion + + #region Serialized. + /// + /// Prefab to spawn for the player. + /// + [Tooltip("Prefab to spawn for the player.")] + [SerializeField] + private NetworkObject _playerPrefab; + /// + /// True to add player to the active scene when no global scenes are specified through the SceneManager. + /// + [Tooltip("True to add player to the active scene when no global scenes are specified through the SceneManager.")] + [SerializeField] + private bool _addToDefaultScene = true; + /// + /// Areas in which players may spawn. + /// + [Tooltip("Areas in which players may spawn.")] + [FormerlySerializedAs("_spawns")] + public Transform[] Spawns = new Transform[0]; + #endregion + + #region Private. + /// + /// NetworkManager on this object or within this objects parents. + /// + private NetworkManager _networkManager; + /// + /// Next spawns to use. + /// + private int _nextSpawn; + #endregion + + private void Start() + { + InitializeOnce(); + } + + private void OnDestroy() + { + if (_networkManager != null) + _networkManager.SceneManager.OnClientLoadedStartScenes -= SceneManager_OnClientLoadedStartScenes; + } + + + /// + /// Initializes this script for use. + /// + private void InitializeOnce() + { + _networkManager = InstanceFinder.NetworkManager; + if (_networkManager == null) + { + Debug.LogWarning($"PlayerSpawner on {gameObject.name} cannot work as NetworkManager wasn't found on this object or within parent objects."); + return; + } + + _networkManager.SceneManager.OnClientLoadedStartScenes += SceneManager_OnClientLoadedStartScenes; + } + + /// + /// Called when a client loads initial scenes after connecting. + /// + private void SceneManager_OnClientLoadedStartScenes(NetworkConnection conn, bool asServer) + { + if (!asServer) + return; + if (_playerPrefab == null) + { + Debug.LogWarning($"Player prefab is empty and cannot be spawned for connection {conn.ClientId}."); + return; + } + + Vector3 position; + Quaternion rotation; + SetSpawn(_playerPrefab.transform, out position, out rotation); + + NetworkObject nob = _networkManager.GetPooledInstantiated(_playerPrefab, true); + nob.transform.SetPositionAndRotation(position, rotation); + _networkManager.ServerManager.Spawn(nob, conn); + + //If there are no global scenes + if (_addToDefaultScene) + _networkManager.SceneManager.AddOwnerToDefaultScene(nob); + + OnSpawned?.Invoke(nob); + } + + + /// + /// Sets a spawn position and rotation. + /// + /// + /// + private void SetSpawn(Transform prefab, out Vector3 pos, out Quaternion rot) + { + //No spawns specified. + if (Spawns.Length == 0) + { + SetSpawnUsingPrefab(prefab, out pos, out rot); + return; + } + + Transform result = Spawns[_nextSpawn]; + if (result == null) + { + SetSpawnUsingPrefab(prefab, out pos, out rot); + } + else + { + pos = result.position; + rot = result.rotation; + } + + //Increase next spawn and reset if needed. + _nextSpawn++; + if (_nextSpawn >= Spawns.Length) + _nextSpawn = 0; + } + + /// + /// Sets spawn using values from prefab. + /// + /// + /// + /// + private void SetSpawnUsingPrefab(Transform prefab, out Vector3 pos, out Quaternion rot) + { + pos = prefab.position; + rot = prefab.rotation; + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/Spawning/PlayerSpawner.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Spawning/PlayerSpawner.cs.meta new file mode 100644 index 0000000..64930b9 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Spawning/PlayerSpawner.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 211a9f6ec51ddc14f908f5acc0cd0423 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/TakeOwnership.meta b/Assets/FishNet/Runtime/Generated/Component/TakeOwnership.meta new file mode 100644 index 0000000..aef58f6 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TakeOwnership.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f4c64a37139b4b946b6f5daf3a7ae3da +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/TakeOwnership/PredictedOwner.cs b/Assets/FishNet/Runtime/Generated/Component/TakeOwnership/PredictedOwner.cs new file mode 100644 index 0000000..7fc3e74 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TakeOwnership/PredictedOwner.cs @@ -0,0 +1,121 @@ +using FishNet.Connection; +using FishNet.Managing; +using FishNet.Managing.Logging; +using FishNet.Managing.Server; +using FishNet.Object; +using FishNet.Object.Synchronizing; +using UnityEngine; + +namespace FishNet.Component.Ownership +{ + /// + /// Adding this component allows any client to take ownership of the object and begin modifying it immediately. + /// + public class PredictedOwner : NetworkBehaviour + { + #region Public. + /// + /// True if the local client used TakeOwnership and is awaiting an ownership change. + /// + public bool TakingOwnership { get; private set; } + /// + /// Owner on client prior to taking ownership. This can be used to reverse a failed ownership attempt. + /// + public NetworkConnection PreviousOwner { get; private set; } = NetworkManager.EmptyConnection; + #endregion + + #region Serialized. + /// + /// True if to enable this component. + /// + [Tooltip("True if to enable this component.")] + [SyncVar(SendRate = 0f)] + [SerializeField] + private bool _allowTakeOwnership = true; + /// + /// Sets the next value for AllowTakeOwnership and synchronizes it. + /// Only the server may use this method. + /// + /// Next value to use. + [Server] + public void SetAllowTakeOwnership(bool value) => _allowTakeOwnership = value; + #endregion + + /// + /// Called on the client after gaining or losing ownership. + /// + /// Previous owner of this object. + public override void OnOwnershipClient(NetworkConnection prevOwner) + { + base.OnOwnershipClient(prevOwner); + /* Unset taken ownership either way. + * If the new owner it won't be used, + * if no longer owner then another client + * took it. */ + TakingOwnership = false; + PreviousOwner = base.Owner; + } + + /// + /// Takes ownership of this object to the local client and allows immediate control. + /// + [Client] + public virtual void TakeOwnership() + { + if (!_allowTakeOwnership) + return; + //Already owner. + if (base.IsOwner) + return; + + NetworkConnection c = base.ClientManager.Connection; + TakingOwnership = true; + //If not server go through the server. + if (!base.IsServer) + { + base.NetworkObject.SetLocalOwnership(c); + ServerTakeOwnership(); + } + //Otherwise take directly without rpcs. + else + { + OnTakeOwnership(c); + } + } + + + /// + /// Takes ownership of this object. + /// + [ServerRpc(RequireOwnership = false)] + private void ServerTakeOwnership(NetworkConnection caller = null) + { + OnTakeOwnership(caller); + } + + /// + /// Called on the server when a client tries to take ownership of this object. + /// + /// Connection trying to take ownership. + [Server] + protected virtual void OnTakeOwnership(NetworkConnection caller) + { + //Client somehow disconnected between here and there. + if (!caller.IsActive) + return; + //Feature is not enabled. + if (!_allowTakeOwnership) + return; + //Already owner. + if (caller == base.Owner) + return; + + base.GiveOwnership(caller); + /* No need to send a response back because an ownershipchange will handle changes. + * Although if you were to override with this your own behavior + * you could send responses for approved/denied. */ + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/TakeOwnership/PredictedOwner.cs.meta b/Assets/FishNet/Runtime/Generated/Component/TakeOwnership/PredictedOwner.cs.meta new file mode 100644 index 0000000..2d0aa92 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TakeOwnership/PredictedOwner.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 03002f7d324007e41b10a9dc87ed3c38 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/TakeOwnership/PredictedSpawn.cs b/Assets/FishNet/Runtime/Generated/Component/TakeOwnership/PredictedSpawn.cs new file mode 100644 index 0000000..307e1a7 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TakeOwnership/PredictedSpawn.cs @@ -0,0 +1,96 @@ +using FishNet.Connection; +using FishNet.Object; +using UnityEngine; + +namespace FishNet.Component.Ownership +{ + /// + /// Adding this component allows any client to use predictive spawning on this prefab. + /// + public class PredictedSpawn : NetworkBehaviour + { + #region Serialized. + /// + /// True to allow clients to predicted spawn this object. + /// + public bool GetAllowSpawning() => _allowSpawning; + /// + /// Sets to allow predicted spawning. This must be set on client and server. + /// + /// New value. + public void SetAllowSpawning(bool value) => _allowSpawning = value; + [Tooltip("True to allow clients to predicted spawn this object.")] + [SerializeField] + private bool _allowSpawning = true; + /// + /// True to allow clients to predicted despawn this object. + /// + public bool GetAllowDespawning() => _allowDespawning; + /// + /// Sets to allow predicted despawning. This must be set on client and server. + /// + /// New value. + public void SetAllowDespawning(bool value) => _allowDespawning = value; + [Tooltip("True to allow clients to predicted despawn this object.")] + [SerializeField] + private bool _allowDespawning = true; + /// + /// + /// + [Tooltip("True to allow clients to predicted set syncTypes prior to spawning the item. Set values will be applied on the server and sent to other clients.")] + [SerializeField] + private bool _allowSyncTypes = true; + /// + /// True to allow clients to predicted set syncTypes prior to spawning the item. Set values will be applied on the server and sent to other clients. + /// + public bool GetAllowSyncTypes() => _allowSyncTypes; + /// + /// Sets to allow syncTypes. This must be set on client and server. + /// + /// New value. + public void SetAllowSyncTypes(bool value) => _allowSyncTypes = value; + #endregion + + /// + /// Called on the client when trying to predicted spawn this object. + /// + /// Owner specified to spawn with. + /// True if able to spawn. + public virtual bool OnTrySpawnClient(NetworkConnection owner = null) + { + return GetAllowSpawning(); + } + /// + /// Called on the server when a client tries to predicted spawn this object. + /// + /// Connection trying to predicted spawn this object. + /// Owner specified to spawn with. + /// True if able to spawn. + public virtual bool OnTrySpawnServer(NetworkConnection spawner, NetworkConnection owner = null) + { + return GetAllowSpawning(); + } + + /// + /// Called on the client when trying to predicted spawn this object. + /// + /// True if able to despawn. + public virtual bool OnTryDespawnClient() + { + return GetAllowDespawning(); + } + /// + /// Called on the server when a client tries to predicted despawn this object. + /// + /// Connection trying to predicted despawn this object. + /// True if able to despawn. + public virtual bool OnTryDepawnServer(NetworkConnection despawner) + { + return GetAllowDespawning(); + } + + + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/TakeOwnership/PredictedSpawn.cs.meta b/Assets/FishNet/Runtime/Generated/Component/TakeOwnership/PredictedSpawn.cs.meta new file mode 100644 index 0000000..1fa856f --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TakeOwnership/PredictedSpawn.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e2b597e1828355a4d994a69cbb11ef85 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/Utility.meta b/Assets/FishNet/Runtime/Generated/Component/Utility.meta new file mode 100644 index 0000000..3fb63eb --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Utility.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4fbf39de0f731b64e97742bcbfa213d3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/Utility/BandwidthDisplay.cs b/Assets/FishNet/Runtime/Generated/Component/Utility/BandwidthDisplay.cs new file mode 100644 index 0000000..c3c9f3b --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Utility/BandwidthDisplay.cs @@ -0,0 +1,192 @@ +using FishNet.Managing.Statistic; +using UnityEngine; + +namespace FishNet.Component.Utility +{ + /// + /// Add to any object to display current ping(round trip time). + /// + [AddComponentMenu("FishNet/Component/BandwidthDisplay")] + public class BandwidthDisplay : MonoBehaviour + { + #region Types. + private enum Corner + { + TopLeft, + TopRight, + BottomLeft, + BottomRight + } + #endregion + + #region Serialized. + /// + /// Color for text. + /// + [Tooltip("Color for text.")] + [SerializeField] + private Color _color = Color.white; + /// + /// Which corner to display network statistics in. + /// + [Tooltip("Which corner to display network statistics in.")] + [SerializeField] + private Corner _placement = Corner.TopRight; + /// + /// rue to show outgoing data bytes. + /// + [Tooltip("True to show outgoing data bytes.")] + [SerializeField] + private bool _showOutgoing = true; + /// + /// Sets ShowOutgoing value. + /// + /// + public void SetShowOutgoing(bool value) => _showOutgoing = value; + /// + /// True to show incoming data bytes. + /// + [Tooltip("True to show incoming data bytes.")] + [SerializeField] + private bool _showIncoming = true; + /// + /// Sets ShowIncoming value. + /// + /// + public void SetShowIncoming(bool value) => _showIncoming = value; + #endregion + + #region Private. + /// + /// Style for drawn ping. + /// + private GUIStyle _style = new GUIStyle(); + /// + /// Text to show for client in/out data. + /// + private string _clientText; + /// + /// Text to show for server in/out data. + /// + private string _serverText; + /// + /// First found NetworkTrafficStatistics. + /// + private NetworkTraficStatistics _networkTrafficStatistics; + #endregion + + private void Start() + { + _networkTrafficStatistics = InstanceFinder.NetworkManager.StatisticsManager.NetworkTraffic; + //Subscribe to both traffic updates. + _networkTrafficStatistics.OnClientNetworkTraffic += NetworkTraffic_OnClientNetworkTraffic; + _networkTrafficStatistics.OnServerNetworkTraffic += NetworkTraffic_OnServerNetworkTraffic; + + if (!_networkTrafficStatistics.UpdateClient && !_networkTrafficStatistics.UpdateServer) + Debug.LogWarning($"StatisticsManager.NetworkTraffic is not updating for client nor server. To see results ensure your NetworkManager has a StatisticsManager component added with the NetworkTraffic values configured."); + } + + private void OnDestroy() + { + if (_networkTrafficStatistics != null) + { + _networkTrafficStatistics.OnClientNetworkTraffic -= NetworkTraffic_OnClientNetworkTraffic; + _networkTrafficStatistics.OnServerNetworkTraffic -= NetworkTraffic_OnClientNetworkTraffic; + } + } + + + /// + /// Called when client network traffic is updated. + /// + private void NetworkTraffic_OnClientNetworkTraffic(NetworkTrafficArgs obj) + { + string nl = System.Environment.NewLine; + string result = string.Empty; + if (_showIncoming) + result += $"Client In: {NetworkTraficStatistics.FormatBytesToLargest(obj.FromServerBytes)}/s{nl}"; + if (_showOutgoing) + result += $"Client Out: {NetworkTraficStatistics.FormatBytesToLargest(obj.ToServerBytes)}/s{nl}"; + + _clientText = result; + } + + /// + /// Called when client network traffic is updated. + /// + private void NetworkTraffic_OnServerNetworkTraffic(NetworkTrafficArgs obj) + { + string nl = System.Environment.NewLine; + string result = string.Empty; + if (_showIncoming) + result += $"Server In: {NetworkTraficStatistics.FormatBytesToLargest(obj.ToServerBytes)}/s{nl}"; + if (_showOutgoing) + result += $"Server Out: {NetworkTraficStatistics.FormatBytesToLargest(obj.FromServerBytes)}/s{nl}"; + + _serverText = result; + } + + + + private void OnGUI() + { + //No need to perform these actions on server. +#if !UNITY_EDITOR && UNITY_SERVER + return; +#endif + + _style.normal.textColor = _color; + _style.fontSize = 15; + + float width = 100f; + float height = 0f; + if (_showIncoming) + height += 15f; + if (_showOutgoing) + height += 15f; + + bool isClient = InstanceFinder.IsClient; + bool isServer = InstanceFinder.IsServer; + if (!isClient) + _clientText = string.Empty; + if (!isServer) + _serverText = string.Empty; + if (isServer && isClient) + height *= 2f; + + float edge = 10f; + + float horizontal; + float vertical; + + if (_placement == Corner.TopLeft) + { + horizontal = 10f; + vertical = 10f; + _style.alignment = TextAnchor.UpperLeft; + } + else if (_placement == Corner.TopRight) + { + horizontal = Screen.width - width - edge; + vertical = 10f; + _style.alignment = TextAnchor.UpperRight; + } + else if (_placement == Corner.BottomLeft) + { + horizontal = 10f; + vertical = Screen.height - height - edge; + _style.alignment = TextAnchor.LowerLeft; + } + else + { + horizontal = Screen.width - width - edge; + vertical = Screen.height - height - edge; + _style.alignment = TextAnchor.LowerRight; + } + + GUI.Label(new Rect(horizontal, vertical, width, height), (_clientText + _serverText), _style); + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/Utility/BandwidthDisplay.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Utility/BandwidthDisplay.cs.meta new file mode 100644 index 0000000..95e27f8 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Utility/BandwidthDisplay.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8bc8f0363ddc75946a958043c5e49a83 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/Utility/DefaultScene.cs b/Assets/FishNet/Runtime/Generated/Component/Utility/DefaultScene.cs new file mode 100644 index 0000000..517be13 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Utility/DefaultScene.cs @@ -0,0 +1,239 @@ +using FishNet.Connection; +using FishNet.Managing; +using FishNet.Managing.Logging; +using FishNet.Managing.Scened; +using FishNet.Transporting; +using FishNet.Utility; +using System.IO; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnitySceneManager = UnityEngine.SceneManagement.SceneManager; + +/// +/// Add to a NetworkManager object to change between Online and Offline scene based on connection states of the server or client. +/// +[AddComponentMenu("FishNet/Component/DefaultScene")] +public class DefaultScene : MonoBehaviour +{ + + #region Serialized. + [Tooltip("True to load the online scene as global, false to load it as connection.")] + [SerializeField] + private bool _useGlobalScenes = true; + /// + /// True to replace all scenes with the offline scene immediately. + /// + [Tooltip("True to replace all scenes with the offline scene immediately.")] + [SerializeField] + private bool _startInOffline; + /// + /// + /// + [Tooltip("Scene to load when disconnected. Server and client will load this scene.")] + [SerializeField, Scene] + private string _offlineScene; + /// + /// Sets which offline scene to use. + /// + /// Scene name to use as the offline scene. + public void SetOfflineScene(string sceneName) => _offlineScene = sceneName; + /// + /// Scene to load when disconnected. Server and client will load this scene. + /// + /// + public string GetOfflineScene() => _offlineScene; + /// + /// + /// + [Tooltip("Scene to load when connected. Server and client will load this scene.")] + [SerializeField, Scene] + private string _onlineScene; + /// + /// Sets which online scene to use. + /// + /// Scene name to use as the online scene. + public void SetOnlineScene(string sceneName) => _onlineScene = sceneName; + /// + /// Scene to load when connected. Server and client will load this scene. + /// + /// + public string GetOnlineScene() => _onlineScene; + /// + /// Which scenes to replace when loading into OnlineScene. + /// + [Tooltip("Which scenes to replace when loading into OnlineScene.")] + [SerializeField] + private ReplaceOption _replaceScenes = ReplaceOption.All; + #endregion + + #region Private. + /// + /// NetworkManager for this component. + /// + private NetworkManager _networkManager; + #endregion + + private void Awake() + { + InitializeOnce(); + } + + private void OnDestroy() + { + + if (!ApplicationState.IsQuitting() && _networkManager != null && _networkManager.Initialized) + { + _networkManager.ClientManager.OnClientConnectionState -= ClientManager_OnClientConnectionState; + _networkManager.ServerManager.OnServerConnectionState -= ServerManager_OnServerConnectionState; + _networkManager.SceneManager.OnLoadEnd -= SceneManager_OnLoadEnd; + _networkManager.ServerManager.OnAuthenticationResult -= ServerManager_OnAuthenticationResult; + } + } + + /// + /// Initializes this script for use. + /// + private void InitializeOnce() + { + _networkManager = GetComponentInParent(); + if (_networkManager == null) + { + + NetworkManager.StaticLogError($"NetworkManager not found on {gameObject.name} or any parent objects. DefaultScene will not work."); + return; + } + //A NetworkManager won't be initialized if it's being destroyed. + if (!_networkManager.Initialized) + return; + if (_onlineScene == string.Empty || _offlineScene == string.Empty) + { + + NetworkManager.StaticLogWarning("Online or Offline scene is not specified. Default scenes will not load."); + return; + } + + _networkManager.ClientManager.OnClientConnectionState += ClientManager_OnClientConnectionState; + _networkManager.ServerManager.OnServerConnectionState += ServerManager_OnServerConnectionState; + _networkManager.SceneManager.OnLoadEnd += SceneManager_OnLoadEnd; + _networkManager.ServerManager.OnAuthenticationResult += ServerManager_OnAuthenticationResult; + if (_startInOffline) + LoadOfflineScene(); + } + + /// + /// Called when a scene load ends. + /// + private void SceneManager_OnLoadEnd(SceneLoadEndEventArgs obj) + { + bool onlineLoaded = false; + foreach (Scene s in obj.LoadedScenes) + { + if (s.name == GetSceneName(_onlineScene)) + { + onlineLoaded = true; + break; + } + } + + //If online scene was loaded then unload offline. + if (onlineLoaded) + UnloadOfflineScene(); + } + + /// + /// Called after the local server connection state changes. + /// + private void ServerManager_OnServerConnectionState(ServerConnectionStateArgs obj) + { + /* When server starts load online scene as global. + * Since this is a global scene clients will automatically + * join it when connecting. */ + if (obj.ConnectionState == LocalConnectionState.Started) + { + /* If not exactly one server is started then + * that means either none are started, which isnt true because + * we just got a started callback, or two+ are started. + * When a server has already started there's no reason to load + * scenes again. */ + if (!_networkManager.ServerManager.OneServerStarted()) + return; + + //If here can load scene. + SceneLoadData sld = new SceneLoadData(GetSceneName(_onlineScene)); + sld.ReplaceScenes = _replaceScenes; + if (_useGlobalScenes) + _networkManager.SceneManager.LoadGlobalScenes(sld); + else + _networkManager.SceneManager.LoadConnectionScenes(sld); + } + //When server stops load offline scene. + else if (obj.ConnectionState == LocalConnectionState.Stopped) + { + LoadOfflineScene(); + } + } + + /// + /// Called after the local client connection state changes. + /// + private void ClientManager_OnClientConnectionState(ClientConnectionStateArgs obj) + { + if (obj.ConnectionState == LocalConnectionState.Stopped) + { + //Only load offline scene if not also server. + if (!_networkManager.IsServer) + LoadOfflineScene(); + } + } + + /// + /// Called when a client completes authentication. + /// + private void ServerManager_OnAuthenticationResult(NetworkConnection arg1, bool authenticated) + { + /* This is only for loading connection scenes. + * If using global there is no need to continue. */ + if (_useGlobalScenes) + return; + if (!authenticated) + return; + + SceneLoadData sld = new SceneLoadData(GetSceneName(_onlineScene)); + _networkManager.SceneManager.LoadConnectionScenes(arg1, sld); + } + + + /// + /// Loads offlineScene as single. + /// + private void LoadOfflineScene() + { + //Already in offline scene. + if (UnitySceneManager.GetActiveScene().name == GetSceneName(_offlineScene)) + return; + //Only use scene manager if networking scenes. I may add something in later to do both local and networked. + UnitySceneManager.LoadScene(_offlineScene); + } + + /// + /// Unloads the offline scene. + /// + private void UnloadOfflineScene() + { + Scene s = UnitySceneManager.GetSceneByName(GetSceneName(_offlineScene)); + if (string.IsNullOrEmpty(s.name)) + return; + + UnitySceneManager.UnloadSceneAsync(s); + } + + /// + /// Returns a scene name from fullPath. + /// + /// + /// + private string GetSceneName(string fullPath) + { + return Path.GetFileNameWithoutExtension(fullPath); + } +} diff --git a/Assets/FishNet/Runtime/Generated/Component/Utility/DefaultScene.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Utility/DefaultScene.cs.meta new file mode 100644 index 0000000..317ec62 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Utility/DefaultScene.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 57ce8bbb58966cb45a7140f32da5327a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/Utility/PingDisplay.cs b/Assets/FishNet/Runtime/Generated/Component/Utility/PingDisplay.cs new file mode 100644 index 0000000..be0f714 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Utility/PingDisplay.cs @@ -0,0 +1,118 @@ +using FishNet.Managing.Timing; +using UnityEngine; + +namespace FishNet.Component.Utility +{ + /// + /// Add to any object to display current ping(round trip time). + /// + [AddComponentMenu("FishNet/Component/PingDisplay")] + public class PingDisplay : MonoBehaviour + { + #region Types. + private enum Corner + { + TopLeft, + TopRight, + BottomLeft, + BottomRight + } + #endregion + + #region Serialized. + /// + /// Color for text. + /// + [Tooltip("Color for text.")] + [SerializeField] + private Color _color = Color.white; + /// + /// Which corner to display ping in. + /// + [Tooltip("Which corner to display ping in.")] + [SerializeField] + private Corner _placement = Corner.TopRight; + /// + /// True to show the real ping. False to include tick rate latency within the ping. + /// + [Tooltip("True to show the real ping. False to include tick rate latency within the ping.")] + [SerializeField] + private bool _hideTickRate = true; + #endregion + + #region Private. + /// + /// Style for drawn ping. + /// + private GUIStyle _style = new GUIStyle(); + #endregion + + private void OnGUI() + { + //No need to perform these actions on server. +#if !UNITY_EDITOR && UNITY_SERVER + return; +#endif + + //Only clients can see pings. + if (!InstanceFinder.IsClient) + return; + + _style.normal.textColor = _color; + _style.fontSize = 15; + float width = 85f; + float height = 15f; + float edge = 10f; + + float horizontal; + float vertical; + + if (_placement == Corner.TopLeft) + { + horizontal = 10f; + vertical = 10f; + } + else if (_placement == Corner.TopRight) + { + horizontal = Screen.width - width - edge; + vertical = 10f; + } + else if (_placement == Corner.BottomLeft) + { + horizontal = 10f; + vertical = Screen.height - height - edge; + } + else + { + horizontal = Screen.width - width - edge; + vertical = Screen.height - height - edge; + } + + long ping; + TimeManager tm = InstanceFinder.TimeManager; + if (tm == null) + { + ping = 0; + } + else + { + ping = tm.RoundTripTime; + long deduction = 0; + if (_hideTickRate) + { + deduction = (long)(tm.TickDelta * 1000d); + /* If host subtract two ticks, if client only subtract one tick. + * This will reflect the users real ping without the tick rate latency. */ + if (InstanceFinder.IsHost) + deduction *= 2; + } + + ping = (long)Mathf.Max(0, ping - deduction); + } + + GUI.Label(new Rect(horizontal, vertical, width, height), $"Ping: {ping}ms", _style); + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/Utility/PingDisplay.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Utility/PingDisplay.cs.meta new file mode 100644 index 0000000..b0748f0 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Utility/PingDisplay.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f9b6b565cd9533c4ebc18003f0fc18a2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/SyncTypes.meta b/Assets/FishNet/Runtime/Generated/SyncTypes.meta new file mode 100644 index 0000000..0f657c7 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/SyncTypes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ece5f63c3df26d44497db8aa5892d46a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/SyncTypes/SyncStopwatch.cs b/Assets/FishNet/Runtime/Generated/SyncTypes/SyncStopwatch.cs new file mode 100644 index 0000000..3b0f35c --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/SyncTypes/SyncStopwatch.cs @@ -0,0 +1,364 @@ +using FishNet.Documenting; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace FishNet.Object.Synchronizing +{ + + /// + /// A SyncObject to efficiently synchronize Stopwatchs over the network. + /// + public class SyncStopwatch : SyncBase, ICustomSync + { + + #region Type. + /// + /// Information about how the Stopwatch has changed. + /// + private struct ChangeData + { + public readonly SyncStopwatchOperation Operation; + public readonly float Previous; + + public ChangeData(SyncStopwatchOperation operation, float previous) + { + Operation = operation; + Previous = previous; + } + } + #endregion + + #region Public. + /// + /// Delegate signature for when the Stopwatch operation occurs. + /// + /// Operation which was performed. + /// Previous value of the Stopwatch. This will be -1f is the value is not available. + /// True if occurring on server. + public delegate void SyncTypeChanged(SyncStopwatchOperation op, float prev, bool asServer); + /// + /// Called when a Stopwatch operation occurs. + /// + public event SyncTypeChanged OnChange; + /// + /// How much time has passed since the Stopwatch started. + /// + public float Elapsed { get; private set; } = -1f; + /// + /// True if the SyncStopwatch is currently paused. Calls to Update(float) will be ignored when paused. + /// + public bool Paused { get; private set; } + #endregion + + #region Private. + /// + /// Changed data which will be sent next tick. + /// + private List _changed = new List(); + /// + /// Server OnChange events waiting for start callbacks. + /// + private readonly List _serverOnChanges = new List(); + /// + /// Client OnChange events waiting for start callbacks. + /// + private readonly List _clientOnChanges = new List(); + #endregion + + /// + /// Starts a Stopwatch. If called when a Stopwatch is already active then StopStopwatch will automatically be sent. + /// + /// Time in which the Stopwatch should start with. + /// True to include remaining time when automatically sending StopStopwatch. + public void StartStopwatch(bool sendElapsedOnStop = true) + { + if (!base.CanNetworkSetValues(true)) + return; + + if (Elapsed > 0f) + StopStopwatch(sendElapsedOnStop); + + Elapsed = 0f; + AddOperation(SyncStopwatchOperation.Start, 0f); + } + + /// + /// Pauses the Stopwatch. Calling while already paused will be result in no action. + /// + /// True to send Remaining with this operation. + public void PauseStopwatch(bool sendElapsed = false) + { + if (Elapsed < 0f) + return; + if (Paused) + return; + if (!base.CanNetworkSetValues(true)) + return; + + Paused = true; + float prev; + SyncStopwatchOperation op; + if (sendElapsed) + { + prev = Elapsed; + op = SyncStopwatchOperation.PauseUpdated; + } + else + { + prev = -1f; + op = SyncStopwatchOperation.Pause; + } + + AddOperation(op, prev); + } + + /// + /// Unpauses the Stopwatch. Calling while already unpaused will be result in no action. + /// + public void UnpauseStopwatch() + { + if (Elapsed < 0f) + return; + if (!Paused) + return; + if (!base.CanNetworkSetValues(true)) + return; + + Paused = false; + AddOperation(SyncStopwatchOperation.Unpause, -1f); + } + + /// + /// Stops and resets the Stopwatch. + /// + public void StopStopwatch(bool sendElapsed = false) + { + if (Elapsed < 0f) + return; + if (!base.CanNetworkSetValues(true)) + return; + + float prev = (sendElapsed) ? -1f : Elapsed; + StopStopwatch_Internal(true); + SyncStopwatchOperation op = (sendElapsed) ? SyncStopwatchOperation.StopUpdated : SyncStopwatchOperation.Stop; + AddOperation(op, prev); + } + + /// + /// Adds an operation to synchronize. + /// + private void AddOperation(SyncStopwatchOperation operation, float prev) + { + if (!base.IsRegistered) + return; + + bool asServerInvoke = (!base.IsNetworkInitialized || base.NetworkBehaviour.IsServer); + + if (asServerInvoke) + { + if (base.Dirty()) + { + ChangeData change = new ChangeData(operation, prev); + _changed.Add(change); + } + } + + OnChange?.Invoke(operation, prev, asServerInvoke); + } + + /// + /// Writes all changed values. + /// + ///True to set the next time data may sync. + public override void WriteDelta(PooledWriter writer, bool resetSyncTick = true) + { + base.WriteDelta(writer, resetSyncTick); + writer.WriteInt32(_changed.Count); + + for (int i = 0; i < _changed.Count; i++) + { + ChangeData change = _changed[i]; + writer.WriteByte((byte)change.Operation); + if (change.Operation == SyncStopwatchOperation.Start) + WriteStartStopwatch(writer, 0f, false); + //Pause and unpause updated need current value written. + //Updated stop also writes current value. + else if (change.Operation == SyncStopwatchOperation.PauseUpdated || change.Operation == SyncStopwatchOperation.StopUpdated) + writer.WriteSingle(change.Previous); + } + + _changed.Clear(); + } + + /// + /// Writes all values. + /// + public override void WriteFull(PooledWriter writer) + { + //Only write full if a Stopwatch is running. + if (Elapsed < 0f) + return; + + base.WriteDelta(writer, false); + + //There will be 1 or 2 entries. If paused 2, if not 1. + int entries = (Paused) ? 2 : 1; + writer.WriteInt32(entries); + //And the operations. + WriteStartStopwatch(writer, Elapsed, true); + if (Paused) + writer.WriteByte((byte)SyncStopwatchOperation.Pause); + } + + /// + /// Writers a start with elapsed time. + /// + /// + private void WriteStartStopwatch(Writer w, float elapsed, bool includeOperationByte) + { + if (includeOperationByte) + w.WriteByte((byte)SyncStopwatchOperation.Start); + + w.WriteSingle(elapsed); + } + + /// + /// Reads and sets the current values for server or client. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [APIExclude] + public override void Read(PooledReader reader, bool asServer) + { + int changes = reader.ReadInt32(); + + for (int i = 0; i < changes; i++) + { + SyncStopwatchOperation op = (SyncStopwatchOperation)reader.ReadByte(); + if (op == SyncStopwatchOperation.Start) + { + float elapsed = reader.ReadSingle(); + if (CanSetValues(asServer)) + Elapsed = elapsed; + InvokeOnChange(op, elapsed, asServer); + } + else if (op == SyncStopwatchOperation.Pause) + { + if (CanSetValues(asServer)) + Paused = true; + InvokeOnChange(op, -1f, asServer); + } + else if (op == SyncStopwatchOperation.PauseUpdated) + { + float prev = reader.ReadSingle(); + if (CanSetValues(asServer)) + Paused = true; + InvokeOnChange(op, prev, asServer); + } + else if (op == SyncStopwatchOperation.Unpause) + { + if (CanSetValues(asServer)) + Paused = false; + InvokeOnChange(op, -1f, asServer); + } + else if (op == SyncStopwatchOperation.Stop) + { + StopStopwatch_Internal(asServer); + InvokeOnChange(op, -1f, false); + } + else if (op == SyncStopwatchOperation.StopUpdated) + { + float prev = reader.ReadSingle(); + StopStopwatch_Internal(asServer); + InvokeOnChange(op, prev, asServer); + } + } + + if (changes > 0) + InvokeOnChange(SyncStopwatchOperation.Complete, -1f, asServer); + } + + /// + /// Returns if values can be updated. + /// + private bool CanSetValues(bool asServer) + { + return (asServer || !base.NetworkManager.IsServer); + } + + /// + /// Stops the Stopwatch and resets. + /// + private void StopStopwatch_Internal(bool asServer) + { + if (!CanSetValues(asServer)) + return; + + Paused = false; + Elapsed = -1f; + } + + + /// + /// Invokes OnChanged callback. + /// + private void InvokeOnChange(SyncStopwatchOperation operation, float prev, bool asServer) + { + if (asServer) + { + if (base.NetworkBehaviour.OnStartServerCalled) + OnChange?.Invoke(operation, prev, asServer); + else + _serverOnChanges.Add(new ChangeData(operation, prev)); + } + else + { + if (base.NetworkBehaviour.OnStartClientCalled) + OnChange?.Invoke(operation, prev, asServer); + else + _clientOnChanges.Add(new ChangeData(operation, prev)); + } + } + + + /// + /// Called after OnStartXXXX has occurred. + /// + /// True if OnStartServer was called, false if OnStartClient. + public override void OnStartCallback(bool asServer) + { + base.OnStartCallback(asServer); + List collection = (asServer) ? _serverOnChanges : _clientOnChanges; + + if (OnChange != null) + { + foreach (ChangeData item in collection) + OnChange.Invoke(item.Operation, item.Previous, asServer); + } + + collection.Clear(); + } + + /// + /// Removes delta from Remaining for server and client. + /// + /// Value to remove from Remaining. + public void Update(float delta) + { + //Not enabled. + if (Elapsed == -1f) + return; + if (Paused) + return; + + Elapsed += delta; + } + + /// + /// Return the serialized type. + /// + /// + public object GetSerializedType() => null; + } +} diff --git a/Assets/FishNet/Runtime/Generated/SyncTypes/SyncStopwatch.cs.meta b/Assets/FishNet/Runtime/Generated/SyncTypes/SyncStopwatch.cs.meta new file mode 100644 index 0000000..813b449 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/SyncTypes/SyncStopwatch.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5b06067fb219c724785a23c4f15e9a2a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/SyncTypes/SyncStopwatchOperation.cs b/Assets/FishNet/Runtime/Generated/SyncTypes/SyncStopwatchOperation.cs new file mode 100644 index 0000000..9ebd17b --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/SyncTypes/SyncStopwatchOperation.cs @@ -0,0 +1,36 @@ +namespace FishNet.Object.Synchronizing +{ + + public enum SyncStopwatchOperation : byte + { + /// + /// Stopwatch is started. Value is included with start. + /// + Start = 1, + /// + /// Stopwatch was paused. + /// + Pause = 2, + /// + /// Stopwatch was paused. Value at time of pause is sent. + /// + PauseUpdated = 3, + /// + /// Stopwatch was unpaused. + /// + Unpause = 4, + /// + /// Stopwatch was stopped. + /// + Stop = 6, + /// + /// Stopwatch was stopped. Value prior to stopping is sent. + /// + StopUpdated = 7, + /// + /// All operations for the tick have been processed. This only occurs on clients as the server is unable to be aware of when the user is done modifying the list. + /// + Complete = 9, + } + +} diff --git a/Assets/FishNet/Runtime/Generated/SyncTypes/SyncStopwatchOperation.cs.meta b/Assets/FishNet/Runtime/Generated/SyncTypes/SyncStopwatchOperation.cs.meta new file mode 100644 index 0000000..0d89456 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/SyncTypes/SyncStopwatchOperation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3ef101e3b1527224ca96ef61c238adbb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/SyncTypes/SyncTimer.cs b/Assets/FishNet/Runtime/Generated/SyncTypes/SyncTimer.cs new file mode 100644 index 0000000..d9f844c --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/SyncTypes/SyncTimer.cs @@ -0,0 +1,407 @@ +using FishNet.Documenting; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace FishNet.Object.Synchronizing +{ + + /// + /// A SyncObject to efficiently synchronize timers over the network. + /// + public class SyncTimer : SyncBase, ICustomSync + { + + #region Type. + /// + /// Information about how the timer has changed. + /// + private struct ChangeData + { + public readonly SyncTimerOperation Operation; + public readonly float Previous; + public readonly float Next; + + public ChangeData(SyncTimerOperation operation, float previous, float next) + { + Operation = operation; + Previous = previous; + Next = next; + } + } + #endregion + + #region Public. + /// + /// Delegate signature for when the timer operation occurs. + /// + /// Operation which was performed. + /// Previous value of the timer. This will be -1f is the value is not available. + /// Value of the timer. This will be -1f is the value is not available. + /// True if occurring on server. + public delegate void SyncTypeChanged(SyncTimerOperation op, float prev, float next, bool asServer); + /// + /// Called when a timer operation occurs. + /// + public event SyncTypeChanged OnChange; + /// + /// Time remaining on the timer. When the timer is expired this value will be 0f. + /// + public float Remaining { get; private set; } + /// + /// How much time has passed since the timer started. + /// + public float Elapsed => (Duration - Remaining); + /// + /// Starting duration of the timer. + /// + public float Duration { get; private set; } + /// + /// True if the SyncTimer is currently paused. Calls to Update(float) will be ignored when paused. + /// + public bool Paused { get; private set; } + #endregion + + #region Private. + /// + /// Changed data which will be sent next tick. + /// + private List _changed = new List(); + /// + /// Server OnChange events waiting for start callbacks. + /// + private readonly List _serverOnChanges = new List(); + /// + /// Client OnChange events waiting for start callbacks. + /// + private readonly List _clientOnChanges = new List(); + + #endregion + + /// + /// Starts a timer. If called when a timer is already active then StopTimer will automatically be sent. + /// + /// Time in which the timer should start with. + /// True to include remaining time when automatically sending StopTimer. + public void StartTimer(float remaining, bool sendRemainingOnStop = true) + { + if (!base.CanNetworkSetValues(true)) + return; + + if (Remaining > 0f) + StopTimer(sendRemainingOnStop); + + Paused = false; + Remaining = remaining; + Duration = remaining; + AddOperation(SyncTimerOperation.Start, -1f, remaining); + } + + /// + /// Pauses the timer. Calling while already paused will be result in no action. + /// + /// True to send Remaining with this operation. + public void PauseTimer(bool sendRemaining = false) + { + if (Remaining <= 0f) + return; + if (Paused) + return; + if (!base.CanNetworkSetValues(true)) + return; + + Paused = true; + SyncTimerOperation op = (sendRemaining) ? SyncTimerOperation.PauseUpdated : SyncTimerOperation.Pause; + AddOperation(op, -1f, -1f); + } + + /// + /// Unpauses the timer. Calling while already unpaused will be result in no action. + /// + public void UnpauseTimer() + { + if (Remaining <= 0f) + return; + if (!Paused) + return; + if (!base.CanNetworkSetValues(true)) + return; + + Paused = false; + AddOperation(SyncTimerOperation.Unpause, -1f, -1f); + } + + /// + /// Stops and resets the timer. + /// + public void StopTimer(bool sendRemaining = false) + { + if (Remaining <= 0f) + return; + if (!base.CanNetworkSetValues(true)) + return; + + bool asServer = true; + float prev = Remaining; + StopTimer_Internal(asServer); + SyncTimerOperation op = (sendRemaining) ? SyncTimerOperation.StopUpdated : SyncTimerOperation.Stop; + AddOperation(op, prev, 0f); + } + + /// + /// Adds an operation to synchronize. + /// + private void AddOperation(SyncTimerOperation operation, float prev, float next) + { + if (!base.IsRegistered) + return; + + bool asServerInvoke = (!base.IsNetworkInitialized || base.NetworkBehaviour.IsServer); + + if (asServerInvoke) + { + if (base.Dirty()) + { + ChangeData change = new ChangeData(operation, prev, next); + _changed.Add(change); + } + } + + OnChange?.Invoke(operation, prev, next, asServerInvoke); + } + + /// + /// Writes all changed values. + /// + ///True to set the next time data may sync. + public override void WriteDelta(PooledWriter writer, bool resetSyncTick = true) + { + base.WriteDelta(writer, resetSyncTick); + writer.WriteInt32(_changed.Count); + + for (int i = 0; i < _changed.Count; i++) + { + ChangeData change = _changed[i]; + writer.WriteByte((byte)change.Operation); + + if (change.Operation == SyncTimerOperation.Start) + { + WriteStartTimer(writer, false); + } + //Pause and unpause updated need current value written. + //Updated stop also writes current value. + else if (change.Operation == SyncTimerOperation.PauseUpdated || change.Operation == SyncTimerOperation.StopUpdated) + { + writer.WriteSingle(change.Next); + } + } + + _changed.Clear(); + } + + /// + /// Writes all values. + /// + public override void WriteFull(PooledWriter writer) + { + //Only write full if a timer is running. + if (Remaining <= 0f) + return; + + base.WriteDelta(writer, false); + //There will be 1 or 2 entries. If paused 2, if not 1. + int entries = (Paused) ? 2 : 1; + writer.WriteInt32(entries); + //And the operations. + WriteStartTimer(writer, true); + if (Paused) + writer.WriteByte((byte)SyncTimerOperation.Pause); + } + + /// + /// Writes a StartTimer operation. + /// + /// + /// + private void WriteStartTimer(Writer w, bool includeOperationByte) + { + if (includeOperationByte) + w.WriteByte((byte)SyncTimerOperation.Start); + w.WriteSingle(Remaining); + w.WriteSingle(Duration); + } + + /// + /// Returns if values can be updated. + /// + private bool CanSetValues(bool asServer) + { + return (asServer || !base.NetworkManager.IsServer); + } + + /// + /// Reads and sets the current values for server or client. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [APIExclude] + public override void Read(PooledReader reader, bool asServer) + { + int changes = reader.ReadInt32(); + + for (int i = 0; i < changes; i++) + { + SyncTimerOperation op = (SyncTimerOperation)reader.ReadByte(); + if (op == SyncTimerOperation.Start) + { + float next = reader.ReadSingle(); + float duration = reader.ReadSingle(); + if (CanSetValues(asServer)) + { + Paused = false; + Remaining = next; + Duration = duration; + } + InvokeOnChange(op, -1f, next, asServer); + } + else if (op == SyncTimerOperation.Pause || op == SyncTimerOperation.PauseUpdated + || op == SyncTimerOperation.Unpause) + { + UpdatePauseState(op); + } + else if (op == SyncTimerOperation.Stop) + { + float prev = Remaining; + StopTimer_Internal(asServer); + InvokeOnChange(op, prev, 0f, false); + } + // + else if (op == SyncTimerOperation.StopUpdated) + { + float prev = Remaining; + float next = reader.ReadSingle(); + StopTimer_Internal(asServer); + InvokeOnChange(op, prev, next, asServer); + } + } + + //Updates a pause state with a pause or unpause operation. + void UpdatePauseState(SyncTimerOperation op) + { + bool newPauseState = (op == SyncTimerOperation.Pause || op == SyncTimerOperation.PauseUpdated); + + float prev = -1f; + float next = -1f; + //If updated time as well. + if (op == SyncTimerOperation.PauseUpdated) + { + prev = Remaining; + next = reader.ReadSingle(); + if (CanSetValues(asServer)) + Remaining = next; + } + + if (CanSetValues(asServer)) + Paused = newPauseState; + InvokeOnChange(op, prev, next, asServer); + } + + if (changes > 0) + InvokeOnChange(SyncTimerOperation.Complete, -1f, -1f, false); + } + + /// + /// Stops the timer and resets. + /// + private void StopTimer_Internal(bool asServer) + { + if (!CanSetValues(asServer)) + return; + + Paused = false; + Remaining = 0f; + } + + + /// + /// Invokes OnChanged callback. + /// + private void InvokeOnChange(SyncTimerOperation operation, float prev, float next, bool asServer) + { + if (asServer) + { + if (base.NetworkBehaviour.OnStartServerCalled) + OnChange?.Invoke(operation, prev, next, asServer); + else + _serverOnChanges.Add(new ChangeData(operation, prev, next)); + } + else + { + if (base.NetworkBehaviour.OnStartClientCalled) + OnChange?.Invoke(operation, prev, next, asServer); + else + _clientOnChanges.Add(new ChangeData(operation, prev, next)); + } + } + + + /// + /// Called after OnStartXXXX has occurred. + /// + /// True if OnStartServer was called, false if OnStartClient. + public override void OnStartCallback(bool asServer) + { + base.OnStartCallback(asServer); + List collection = (asServer) ? _serverOnChanges : _clientOnChanges; + + if (OnChange != null) + { + foreach (ChangeData item in collection) + OnChange.Invoke(item.Operation, item.Previous, item.Next, asServer); + } + + collection.Clear(); + } + + /// + /// Removes delta from Remaining for server and client. + /// + /// Value to remove from Remaining. + public void Update(float delta) + { + //Not enabled. + if (Remaining <= 0f) + return; + if (Paused) + return; + + if (delta < 0) + delta *= -1f; + float prev = Remaining; + Remaining -= delta; + //Still time left. + if (Remaining > 0f) + return; + + /* If here then the timer has + * ended. Invoking the events is tricky + * here because both the server and the client + * would share the same value. Because of this check + * if each socket is started and if so invoke for that + * side. There's a chance down the road this may need to be improved + * for some but at this time I'm unable to think of any + * problems. */ + Remaining = 0f; + if (base.NetworkManager.IsServer) + OnChange?.Invoke(SyncTimerOperation.Finished, prev, 0f, true); + if (base.NetworkManager.IsClient) + OnChange?.Invoke(SyncTimerOperation.Finished, prev, 0f, false); + } + + /// + /// Return the serialized type. + /// + /// + public object GetSerializedType() => null; + } +} diff --git a/Assets/FishNet/Runtime/Generated/SyncTypes/SyncTimer.cs.meta b/Assets/FishNet/Runtime/Generated/SyncTypes/SyncTimer.cs.meta new file mode 100644 index 0000000..d513c5e --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/SyncTypes/SyncTimer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6abdbcaa07147024fbe99c2813126910 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/SyncTypes/SyncTimerOperation.cs b/Assets/FishNet/Runtime/Generated/SyncTypes/SyncTimerOperation.cs new file mode 100644 index 0000000..af95e7b --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/SyncTypes/SyncTimerOperation.cs @@ -0,0 +1,40 @@ +namespace FishNet.Object.Synchronizing +{ + + public enum SyncTimerOperation : byte + { + /// + /// Timer is started. Value is included with start. + /// + Start = 1, + /// + /// Timer was paused. + /// + Pause = 2, + /// + /// Timer was paused. Value at time of pause is sent. + /// + PauseUpdated = 3, + /// + /// Timer was unpaused. + /// + Unpause = 4, + /// + /// Timer was stopped. + /// + Stop = 6, + /// + /// Timer was stopped. Value prior to stopping is sent. + /// + StopUpdated = 7, + /// + /// The timer has ended finished it's duration. + /// + Finished = 8, + /// + /// All operations for the tick have been processed. This only occurs on clients as the server is unable to be aware of when the user is done modifying the list. + /// + Complete = 9, + } + +} diff --git a/Assets/FishNet/Runtime/Generated/SyncTypes/SyncTimerOperation.cs.meta b/Assets/FishNet/Runtime/Generated/SyncTypes/SyncTimerOperation.cs.meta new file mode 100644 index 0000000..ee8e489 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/SyncTypes/SyncTimerOperation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c7732c822303d3c4387a4af44467e791 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/InstanceFinder.cs b/Assets/FishNet/Runtime/InstanceFinder.cs new file mode 100644 index 0000000..20d230b --- /dev/null +++ b/Assets/FishNet/Runtime/InstanceFinder.cs @@ -0,0 +1,219 @@ +using FishNet.Component.ColliderRollback; +using FishNet.Managing; +using FishNet.Managing.Client; +using FishNet.Managing.Predicting; +using FishNet.Managing.Scened; +using FishNet.Managing.Server; +using FishNet.Managing.Statistic; +using FishNet.Managing.Timing; +using FishNet.Managing.Transporting; +using FishNet.Utility; +using System; +using System.Linq; +using UnityEngine; + +namespace FishNet +{ + + /// + /// Used to globally get information from the first found instance of NetworkManager. + /// + public static class InstanceFinder + { + + #region Public. + /// + /// Returns the first found NetworkManager instance. + /// + public static NetworkManager NetworkManager + { + get + { + if (_networkManager == null) + { + int managersCount = NetworkManager.Instances.Count; + //At least one manager. + if (managersCount > 0) + { + _networkManager = NetworkManager.Instances.First(); + if (managersCount > 1) + _networkManager.LogWarning($"Multiple NetworkManagers found, the first result will be returned. If you only wish to have one NetworkManager then uncheck 'Allow Multiple' within your NetworkManagers."); + } + //No managers. + else + { + //If application is quitting return null without logging. + if (ApplicationState.IsQuitting()) + return null; + + Debug.Log($"NetworkManager not found in any open scenes."); + } + } + + return _networkManager; + } + } + + /// + /// Returns the first instance of ServerManager. + /// + public static ServerManager ServerManager + { + get + { + NetworkManager nm = NetworkManager; + return (nm == null) ? null : nm.ServerManager; + } + } + + /// + /// Returns the first instance of ClientManager. + /// + public static ClientManager ClientManager + { + get + { + NetworkManager nm = NetworkManager; + return (nm == null) ? null : nm.ClientManager; + } + } + + /// + /// Returns the first instance of TransportManager. + /// + public static TransportManager TransportManager + { + get + { + NetworkManager nm = NetworkManager; + return (nm == null) ? null : nm.TransportManager; + } + } + + /// + /// Returns the first instance of TimeManager. + /// + public static TimeManager TimeManager + { + get + { + NetworkManager nm = NetworkManager; + return (nm == null) ? null : nm.TimeManager; + } + } + + /// + /// Returns the first instance of SceneManager. + /// + public static SceneManager SceneManager + { + get + { + NetworkManager nm = NetworkManager; + return (nm == null) ? null : nm.SceneManager; + } + } + /// + /// Returns the first instance of RollbackManager. + /// + public static RollbackManager RollbackManager + { + get + { + NetworkManager nm = NetworkManager; + return (nm == null) ? null : nm.RollbackManager; + } + } + /// + /// Returns the first instance of PredictionManager. + /// + public static PredictionManager PredictionManager + { + get + { + NetworkManager nm = NetworkManager; + return (nm == null) ? null : nm.PredictionManager; + } + } + /// + /// Returns the first instance of StatisticsManager. + /// + public static StatisticsManager StatisticsManager + { + get + { + NetworkManager nm = NetworkManager; + return (nm == null) ? null : nm.StatisticsManager; + } + } + + /// + /// True if the server is active. + /// + public static bool IsServer => (NetworkManager == null) ? false : NetworkManager.IsServer; + /// + /// True if only the server is active. + /// + public static bool IsServerOnly => (NetworkManager == null) ? false : NetworkManager.IsServerOnly; + /// + /// True if the client is active and authenticated. + /// + public static bool IsClient => (NetworkManager == null) ? false : NetworkManager.IsClient; + /// + /// True if only the client is active and authenticated. + /// + public static bool IsClientOnly => (NetworkManager == null) ? false : NetworkManager.IsClientOnly; + /// + /// True if client and server are active. + /// + public static bool IsHost => (NetworkManager == null) ? false : NetworkManager.IsHost; + /// + /// True if client nor server are active. + /// + public static bool IsOffline => (_networkManager == null) ? true : (!NetworkManager.IsServer && !NetworkManager.IsClient); + #endregion + + #region Private. + /// + /// NetworkManager instance. + /// + private static NetworkManager _networkManager; + #endregion + + #region Registered components + /// + /// Registers to invoke an action when a specified component becomes registered. Action will invoke immediately if already registered. + /// + /// Component type. + /// Action to invoke. + public static void RegisterInvokeOnInstance(Action handler) where T : UnityEngine.Component => NetworkManager?.RegisterInvokeOnInstance(handler); + /// + /// Unrgisters to invoke an action when a specified component becomes registered. Action will invoke immediately if already registered. + /// + /// Component type. + /// Action to invoke. + public static void UnregisterInvokeOnInstance(Action handler) where T : UnityEngine.Component => NetworkManager?.UnregisterInvokeOnInstance(handler); + /// + /// Returns class of type if found within CodegenBase classes. + /// + /// + /// + public static T GetInstance() where T : UnityEngine.Component => NetworkManager?.GetInstance(); + /// + /// Registers a new component to this NetworkManager. + /// + /// Type to register. + /// Reference of the component being registered. + /// True to replace existing references. + public static void RegisterInstance(T component, bool replace = true) where T : UnityEngine.Component => NetworkManager?.RegisterInstance(component, replace); + /// + /// Unregisters a component from this NetworkManager. + /// + /// Type to unregister. + public static void UnregisterInstance() where T : UnityEngine.Component => NetworkManager?.UnregisterInstance(); + #endregion + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/InstanceFinder.cs.meta b/Assets/FishNet/Runtime/InstanceFinder.cs.meta new file mode 100644 index 0000000..97dc53c --- /dev/null +++ b/Assets/FishNet/Runtime/InstanceFinder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 528668525d755164d989cddc73e3eee5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing.meta b/Assets/FishNet/Runtime/Managing.meta new file mode 100644 index 0000000..f77c46d --- /dev/null +++ b/Assets/FishNet/Runtime/Managing.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2881da549094ec241b3a289e6151869e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Client.meta b/Assets/FishNet/Runtime/Managing/Client.meta new file mode 100644 index 0000000..1d0fe06 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 96f81f64930875143840663a13e78564 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Client/ClientManager.Broadcast.cs b/Assets/FishNet/Runtime/Managing/Client/ClientManager.Broadcast.cs new file mode 100644 index 0000000..c45b32a --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/ClientManager.Broadcast.cs @@ -0,0 +1,179 @@ +using FishNet.Broadcast; +using FishNet.Broadcast.Helping; +using FishNet.Managing.Logging; +using FishNet.Managing.Utility; +using FishNet.Object.Helping; +using FishNet.Serializing; +using FishNet.Serializing.Helping; +using FishNet.Transporting; +using FishNet.Utility.Extension; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Managing.Client +{ + public sealed partial class ClientManager : MonoBehaviour + { + #region Private. + /// + /// Delegate to read received broadcasts. + /// + /// + private delegate void ServerBroadcastDelegate(PooledReader reader); + /// + /// Delegates for each key. + /// + private readonly Dictionary> _broadcastHandlers = new Dictionary>(); + /// + /// Delegate targets for each key. + /// + private Dictionary> _handlerTargets = new Dictionary>(); + #endregion + + /// + /// Registers a method to call when a Broadcast arrives. + /// + /// Type of broadcast being registered. + /// Method to call. + public void RegisterBroadcast(Action handler) where T : struct, IBroadcast + { + ushort key = typeof(T).FullName.GetStableHash16(); + /* Create delegate and add for + * handler method. */ + HashSet handlers; + if (!_broadcastHandlers.TryGetValueIL2CPP(key, out handlers)) + { + handlers = new HashSet(); + _broadcastHandlers.Add(key, handlers); + } + ServerBroadcastDelegate del = CreateBroadcastDelegate(handler); + handlers.Add(del); + + /* Add hashcode of target for handler. + * This is so we can unregister the target later. */ + int handlerHashCode = handler.GetHashCode(); + HashSet<(int, ServerBroadcastDelegate)> targetHashCodes; + if (!_handlerTargets.TryGetValueIL2CPP(key, out targetHashCodes)) + { + targetHashCodes = new HashSet<(int, ServerBroadcastDelegate)>(); + _handlerTargets.Add(key, targetHashCodes); + } + + targetHashCodes.Add((handlerHashCode, del)); + } + + /// + /// Unregisters a method call from a Broadcast type. + /// + /// Type of broadcast being unregistered. + /// Method to unregister. + public void UnregisterBroadcast(Action handler) where T : struct, IBroadcast + { + ushort key = BroadcastHelper.GetKey(); + + /* If key is found for T then look for + * the appropriate handler to remove. */ + if (_broadcastHandlers.TryGetValueIL2CPP(key, out HashSet handlers)) + { + HashSet<(int, ServerBroadcastDelegate)> targetHashCodes; + if (_handlerTargets.TryGetValueIL2CPP(key, out targetHashCodes)) + { + int handlerHashCode = handler.GetHashCode(); + ServerBroadcastDelegate result = null; + foreach ((int targetHashCode, ServerBroadcastDelegate del) in targetHashCodes) + { + if (targetHashCode == handlerHashCode) + { + result = del; + targetHashCodes.Remove((targetHashCode, del)); + break; + } + } + //If no more in targetHashCodes then remove from handlerTarget. + if (targetHashCodes.Count == 0) + _handlerTargets.Remove(key); + + if (result != null) + handlers.Remove(result); + } + + //If no more in handlers then remove broadcastHandlers. + if (handlers.Count == 0) + _broadcastHandlers.Remove(key); + } + } + + /// + /// Creates a ServerBroadcastDelegate. + /// + /// + /// + /// + /// + private ServerBroadcastDelegate CreateBroadcastDelegate(Action handler) + { + void LogicContainer(PooledReader reader) + { + T broadcast = reader.Read(); + handler?.Invoke(broadcast); + } + return LogicContainer; + } + + /// + /// Parses a received broadcast. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ParseBroadcast(PooledReader reader, Channel channel) + { + ushort key = reader.ReadUInt16(); + int dataLength = Packets.GetPacketLength((ushort)PacketId.Broadcast, reader, channel); + // try to invoke the handler for that message + if (_broadcastHandlers.TryGetValueIL2CPP(key, out HashSet handlers)) + { + int readerStartPosition = reader.Position; + /* //muchlater resetting the position could be better by instead reading once and passing in + * the object to invoke with. */ + foreach (ServerBroadcastDelegate handler in handlers) + { + reader.Position = readerStartPosition; + handler.Invoke(reader); + } + } + else + { + reader.Skip(dataLength); + } + } + + + /// + /// Sends a Broadcast to the server. + /// + /// Type of broadcast to send. + /// Broadcast data being sent; for example: an instance of your broadcast type. + /// Channel to send on. + public void Broadcast(T message, Channel channel = Channel.Reliable) where T : struct, IBroadcast + { + //Check local connection state. + if (!Started) + { + NetworkManager.LogWarning($"Cannot send broadcast to server because client is not active."); + return; + } + + using (PooledWriter writer = WriterPool.GetWriter()) + { + Broadcasts.WriteBroadcast(writer, message, channel); + ArraySegment segment = writer.GetArraySegment(); + + NetworkManager.TransportManager.SendToServer((byte)channel, segment); + } + } + + } + + +} diff --git a/Assets/FishNet/Runtime/Managing/Client/ClientManager.Broadcast.cs.meta b/Assets/FishNet/Runtime/Managing/Client/ClientManager.Broadcast.cs.meta new file mode 100644 index 0000000..6b82d50 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/ClientManager.Broadcast.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 97ef4be4cfcc6a54d80a65a5c8d325d7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Client/ClientManager.LOD.cs b/Assets/FishNet/Runtime/Managing/Client/ClientManager.LOD.cs new file mode 100644 index 0000000..437132d --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/ClientManager.LOD.cs @@ -0,0 +1,197 @@ +using FishNet.Managing.Timing; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Transporting; +using System.Collections.Generic; +using UnityEngine; + +namespace FishNet.Managing.Client +{ + public sealed partial class ClientManager : MonoBehaviour + { + #region Internal. + /// + /// How many ticks between each LOD update. + /// + public uint LevelOfDetailInterval => NetworkManager.TimeManager.TimeToTicks(0.5d, TickRounding.RoundUp); + #endregion + + #region Private. + /// + /// Positions of the player objects. + /// + private List _objectsPositionsCache = new List(); + /// + /// Next index within Spawned to update the LOD on. + /// + private int _nextLodNobIndex; + #endregion + + /// + /// Sends a level of update if conditions are met. + /// + /// True to force send a full update immediately. + /// This may be useful when teleporting clients. + /// This must be called by on the server by using ServerManager.ForceLodUpdate(NetworkConnection). + /// + internal void SendLodUpdate(bool forceFullUpdate) + { + if (!Connection.Authenticated) + return; + NetworkManager nm = NetworkManager; + if (forceFullUpdate) + { + nm.LogError($"ForceFullUpdate is not yet implemented. Setting this true should not be possible."); + return; + } + if (!nm.ObserverManager.GetUseNetworkLod()) + return; + + //Interval check. + uint localTick = nm.TimeManager.LocalTick; + uint intervalRequirement = LevelOfDetailInterval; + bool intervalMet = ((localTick - Connection.LastLevelOfDetailUpdate) >= intervalRequirement); + if (!forceFullUpdate && !intervalMet) + return; + + //Set next tick. + Connection.LastLevelOfDetailUpdate = localTick; + + List localClientSpawned = nm.ClientManager.Objects.LocalClientSpawned; + int spawnedCount = localClientSpawned.Count; + if (spawnedCount == 0) + return; + + //Rebuild position cache for players objects. + _objectsPositionsCache.Clear(); + foreach (NetworkObject playerObjects in Connection.Objects) + _objectsPositionsCache.Add(playerObjects.transform.position); + + /* Set the maximum number of entries per send. + * Each send is going to be approximately 3 bytes + * but sometimes can be 4. Calculate based off the maximum + * possible bytes. */ + //int mtu = NetworkManager.TransportManager.GetMTU((byte)Channel.Reliable); + const int estimatedMaximumIterations = ( 400 / 4); + /* Aim to process all objects over at most 10 seconds. + * To reduce the number of packets sent objects are + * calculated ~twice a second. This means if the client had + * 1000 objects visible to them they would need to process + * 100 objects a second, so 50 objects every half a second. + * This should be no problem even on slower mobile devices. */ + int iterations; + //Normal update. + if (!forceFullUpdate) + { + iterations = Mathf.Min(spawnedCount, estimatedMaximumIterations); + } + //Force does a full update. + else + { + _nextLodNobIndex = 0; + iterations = spawnedCount; + } + + //Cache a few more things. + Dictionary currentLods = Connection.LevelOfDetails; + List lodDistances = NetworkManager.ObserverManager.GetLevelOfDetailDistances(); + + //Index to use next is too high so reset it. + if (_nextLodNobIndex >= spawnedCount) + _nextLodNobIndex = 0; + int nobIndex = _nextLodNobIndex; + + PooledWriter tmpWriter = WriterPool.GetWriter(1000); + int written = 0; + + //Only check if player has objects. + if (_objectsPositionsCache.Count > 0) + { + for (int i = 0; i < iterations; i++) + { + NetworkObject nob = localClientSpawned[nobIndex]; + //Somehow went null. Can occur perhaps if client destroys objects between ticks maybe. + if (nob == null) + { + IncreaseObjectIndex(); + continue; + } + //Only check objects not owned by the local client. + if (!nob.IsOwner && !nob.IsDeinitializing) + { + Vector3 nobPosition = nob.transform.position; + float closestDistance = float.MaxValue; + foreach (Vector3 objPosition in _objectsPositionsCache) + { + float dist = Vector3.SqrMagnitude(nobPosition - objPosition); + if (dist < closestDistance) + closestDistance = dist; + } + + //If not within any distances then max lod will be used, the value below. + byte lod = (byte)(lodDistances.Count - 1); + for (byte z = 0; z < lodDistances.Count; z++) + { + //Distance is within range of this lod. + if (closestDistance <= lodDistances[z]) + { + lod = z; + break; + } + } + + bool changed; + /* See if value changed. Value is changed + * if it's not the same of old or if + * the nob has not yet been added to the + * level of details collection. + * Even if a forced update only delta + * needs to send. */ + if (currentLods.TryGetValue(nob, out byte oldLod)) + changed = (oldLod != lod); + else + changed = true; + + //If changed then set new value and write. + if (changed) + { + currentLods[nob] = lod; + tmpWriter.WriteNetworkObjectId(nob.ObjectId); + tmpWriter.WriteByte(lod); + written++; + } + } + + IncreaseObjectIndex(); + + void IncreaseObjectIndex() + { + nobIndex++; + if (nobIndex >= spawnedCount) + nobIndex = 0; + } + } + } + + //Set next lod index to current nob index. + _nextLodNobIndex = nobIndex; + /* Send using the reliable channel since + * we are using deltas. This is also why + * updates are sent larger chunked twice a second rather + * than smaller chunks regularly. */ + PooledWriter writer = WriterPool.GetWriter(1000); + writer.WritePacketId(PacketId.NetworkLODUpdate); + writer.WriteInt32(written); + writer.WriteArraySegment(tmpWriter.GetArraySegment()); + NetworkManager.TransportManager.SendToServer((byte)Channel.Reliable, writer.GetArraySegment(), true); + + //Dispose writers. + writer.DisposeLength(); + tmpWriter.DisposeLength(); + } + + + } + + +} diff --git a/Assets/FishNet/Runtime/Managing/Client/ClientManager.LOD.cs.meta b/Assets/FishNet/Runtime/Managing/Client/ClientManager.LOD.cs.meta new file mode 100644 index 0000000..c7469a2 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/ClientManager.LOD.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 249df1192f06801488309cd13fd2b8ef +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs b/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs new file mode 100644 index 0000000..ef9be20 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs @@ -0,0 +1,503 @@ +using FishNet.Connection; +using FishNet.Managing.Debugging; +using FishNet.Managing.Logging; +using FishNet.Managing.Server; +using FishNet.Managing.Transporting; +using FishNet.Serializing; +using FishNet.Transporting; +using FishNet.Utility.Extension; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace FishNet.Managing.Client +{ + /// + /// A container for local client data and actions. + /// + [DisallowMultipleComponent] + [AddComponentMenu("FishNet/Manager/ClientManager")] + public sealed partial class ClientManager : MonoBehaviour + { + #region Public. + /// + /// Called after local client has authenticated. + /// + public event Action OnAuthenticated; + /// + /// Called after the local client connection state changes. + /// + public event Action OnClientConnectionState; + /// + /// Called when a client other than self connects. + /// This is only available when using ServerManager.ShareIds. + /// + public event Action OnRemoteConnectionState; + /// + /// True if the client connection is connected to the server. + /// + public bool Started { get; private set; } + /// + /// NetworkConnection the local client is using to send data to the server. + /// + public NetworkConnection Connection = NetworkManager.EmptyConnection; + /// + /// Handling and information for objects known to the local client. + /// + public ClientObjects Objects { get; private set; } + /// + /// All currently connected clients. This field only contains data while ServerManager.ShareIds is enabled. + /// + public Dictionary Clients = new Dictionary(); + /// + /// NetworkManager for client. + /// + [HideInInspector] + public NetworkManager NetworkManager { get; private set; } + #endregion + + #region Serialized. + /// + /// True to automatically set the frame rate when the client connects. + /// + [Tooltip("True to automatically set the frame rate when the client connects.")] + [SerializeField] + private bool _changeFrameRate = true; + /// + /// + /// + [Tooltip("Maximum frame rate the client may run at. When as host this value runs at whichever is higher between client and server.")] + [Range(1, NetworkManager.MAXIMUM_FRAMERATE)] + [SerializeField] + private ushort _frameRate = NetworkManager.MAXIMUM_FRAMERATE; + /// + /// Maximum frame rate the client may run at. When as host this value runs at whichever is higher between client and server. + /// + internal ushort FrameRate => (_changeFrameRate) ? _frameRate : (ushort)0; + #endregion + + #region Private. + /// + /// Used to read splits. + /// + private SplitReader _splitReader = new SplitReader(); +#if UNITY_EDITOR || DEVELOPMENT_BUILD + /// + /// Logs data about parser to help debug. + /// + private ParseLogger _parseLogger = new ParseLogger(); +#endif + #endregion + + private void OnDestroy() + { + Objects?.SubscribeToSceneLoaded(false); + } + + + /// + /// Initializes this script for use. + /// + /// + internal void InitializeOnce_Internal(NetworkManager manager) + { + NetworkManager = manager; + Objects = new ClientObjects(manager); + Objects.SubscribeToSceneLoaded(true); + /* Unsubscribe before subscribing. + * Shouldn't be an issue but better safe than sorry. */ + SubscribeToEvents(false); + SubscribeToEvents(true); + //Listen for client connections from server. + RegisterBroadcast(OnClientConnectionBroadcast); + RegisterBroadcast(OnConnectedClientsBroadcast); + } + + + /// + /// Called when the server sends a connection state change for any client. + /// + /// + private void OnClientConnectionBroadcast(ClientConnectionChangeBroadcast args) + { + //If connecting invoke after added to clients, otherwise invoke before removed. + RemoteConnectionStateArgs rcs = new RemoteConnectionStateArgs((args.Connected) ? RemoteConnectionState.Started : RemoteConnectionState.Stopped, args.Id, -1); + + if (args.Connected) + { + Clients[args.Id] = new NetworkConnection(NetworkManager, args.Id, false); + OnRemoteConnectionState?.Invoke(rcs); + } + else + { + OnRemoteConnectionState?.Invoke(rcs); + if (Clients.TryGetValue(args.Id, out NetworkConnection c)) + { + c.Dispose(); + Clients.Remove(args.Id); + } + } + } + + /// + /// Called when the server sends all currently connected clients. + /// + /// + private void OnConnectedClientsBroadcast(ConnectedClientsBroadcast args) + { + NetworkManager.ClearClientsCollection(Clients); + + List collection = args.ListCache.Collection;// args.Ids; + //No connected clients except self. + if (collection == null) + return; + + int count = collection.Count; + for (int i = 0; i < count; i++) + { + int id = collection[i]; + Clients[id] = new NetworkConnection(NetworkManager, id, false); + } + } + + /// + /// Changes subscription status to transport. + /// + /// + private void SubscribeToEvents(bool subscribe) + { + if (NetworkManager == null || NetworkManager.TransportManager == null || NetworkManager.TransportManager.Transport == null) + return; + + if (subscribe) + { + NetworkManager.TransportManager.OnIterateIncomingEnd += TransportManager_OnIterateIncomingEnd; + NetworkManager.TransportManager.Transport.OnClientReceivedData += Transport_OnClientReceivedData; + NetworkManager.TransportManager.Transport.OnClientConnectionState += Transport_OnClientConnectionState; + } + else + { + NetworkManager.TransportManager.OnIterateIncomingEnd -= TransportManager_OnIterateIncomingEnd; + NetworkManager.TransportManager.Transport.OnClientReceivedData -= Transport_OnClientReceivedData; + NetworkManager.TransportManager.Transport.OnClientConnectionState -= Transport_OnClientConnectionState; + } + } + + /// + /// Stops the local client connection. + /// + public void StopConnection() + { + NetworkManager.TransportManager.Transport.StopConnection(false); + } + + /// + /// Starts the local client connection. + /// + public void StartConnection() + { + NetworkManager.TransportManager.Transport.StartConnection(false); + } + + /// + /// Sets the transport address and starts the local client connection. + /// + public void StartConnection(string address) + { + StartConnection(address, NetworkManager.TransportManager.Transport.GetPort()); + } + /// + /// Sets the transport address and port, and starts the local client connection. + /// + public void StartConnection(string address, ushort port) + { + NetworkManager.TransportManager.Transport.SetClientAddress(address); + NetworkManager.TransportManager.Transport.SetPort(port); + StartConnection(); + } + + /// + /// Called when a connection state changes for the local client. + /// + /// + private void Transport_OnClientConnectionState(ClientConnectionStateArgs args) + { + LocalConnectionState state = args.ConnectionState; + Started = (state == LocalConnectionState.Started); + Objects.OnClientConnectionState(args); + + //Clear connection after so objects can update using current Connection value. + if (!Started) + { + Connection = NetworkManager.EmptyConnection; + NetworkManager.ClearClientsCollection(Clients); + } + + if (NetworkManager.CanLog(LoggingType.Common)) + { + Transport t = NetworkManager.TransportManager.GetTransport(args.TransportIndex); + string tName = (t == null) ? "Unknown" : t.GetType().Name; + Debug.Log($"Local client is {state.ToString().ToLower()} for {tName}."); + } + + NetworkManager.UpdateFramerate(); + OnClientConnectionState?.Invoke(args); + } + + /// + /// Called when a socket receives data. + /// + private void Transport_OnClientReceivedData(ClientReceivedDataArgs args) + { + args.Data = NetworkManager.TransportManager.ProcessIntermediateIncoming(args.Data, true); + ParseReceived(args); + } + + /// + /// Called after IterateIncoming has completed. + /// + private void TransportManager_OnIterateIncomingEnd(bool server) + { + /* Should the last packet received be a spawn or despawn + * then the cache won't yet be iterated because it only + * iterates when a packet is anything but those two. Because + * of such if any object caches did come in they must be iterated + * at the end of the incoming cycle. This isn't as clean as I'd + * like but it does ensure there will be no missing network object + * references on spawned objects. */ + if (Started && !server) + Objects.IterateObjectCache(); + } + + /// + /// Parses received data. + /// + private void ParseReceived(ClientReceivedDataArgs args) + { +#if UNITY_EDITOR || DEVELOPMENT_BUILD + _parseLogger.Reset(); +#endif + + ArraySegment segment = args.Data; + NetworkManager.StatisticsManager.NetworkTraffic.LocalClientReceivedData((ulong)segment.Count); + if (segment.Count <= TransportManager.TICK_BYTES) + return; + + PacketId packetId = PacketId.Unset; +#if !UNITY_EDITOR && !DEVELOPMENT_BUILD + try + { +#endif + using (PooledReader reader = ReaderPool.GetReader(segment, NetworkManager)) + { + NetworkManager.TimeManager.LastPacketTick = reader.ReadUInt32(AutoPackType.Unpacked); + /* This is a special condition where a message may arrive split. + * When this occurs buffer each packet until all packets are + * received. */ + if (reader.PeekPacketId() == PacketId.Split) + { + //Skip packetId. + reader.ReadPacketId(); + int expectedMessages; + _splitReader.GetHeader(reader, out expectedMessages); + _splitReader.Write(NetworkManager.TimeManager.LastPacketTick, reader, expectedMessages); + /* If fullMessage returns 0 count then the split + * has not written fully yet. Otherwise, if there is + * data within then reinitialize reader with the + * full message. */ + ArraySegment fullMessage = _splitReader.GetFullMessage(); + if (fullMessage.Count == 0) + return; + + //Initialize reader with full message. + reader.Initialize(fullMessage, NetworkManager); + } + + while (reader.Remaining > 0) + { + packetId = reader.ReadPacketId(); +#if UNITY_EDITOR || DEVELOPMENT_BUILD + _parseLogger.AddPacket(packetId); +#endif + bool spawnOrDespawn = (packetId == PacketId.ObjectSpawn || packetId == PacketId.ObjectDespawn); + /* Length of data. Only available if using unreliable. Unreliable packets + * can arrive out of order which means object orientated messages such as RPCs may + * arrive after the object for which they target has already been destroyed. When this happens + * on lesser solutions they just dump the entire packet. However, since FishNet batches data. + * it's very likely a packet will contain more than one packetId. With this mind, length is + * sent as well so if any reason the data does have to be dumped it will only be dumped for + * that single packetId but not the rest. Broadcasts don't need length either even if unreliable + * because they are not object bound. */ + + //Is spawn or despawn; cache packet. + if (spawnOrDespawn) + { + if (packetId == PacketId.ObjectSpawn) + Objects.CacheSpawn(reader); + else if (packetId == PacketId.ObjectDespawn) + Objects.CacheDespawn(reader); + } + //Not spawn or despawn. + else + { + /* Iterate object cache should any of the + * incoming packets rely on it. Objects + * in cache will always be received before any messages + * that use them. */ + Objects.IterateObjectCache(); + //Then process packet normally. + if ((ushort)packetId >= NetworkManager.StartingRpcLinkIndex) + { + Objects.ParseRpcLink(reader, (ushort)packetId, args.Channel); + } + else if (packetId == PacketId.Reconcile) + { + Objects.ParseReconcileRpc(reader, args.Channel); + } + else if (packetId == PacketId.ObserversRpc) + { + Objects.ParseObserversRpc(reader, args.Channel); + } + else if (packetId == PacketId.TargetRpc) + { + Objects.ParseTargetRpc(reader, args.Channel); + } + else if (packetId == PacketId.Broadcast) + { + ParseBroadcast(reader, args.Channel); + } + else if (packetId == PacketId.PingPong) + { + ParsePingPong(reader); + } + else if (packetId == PacketId.SyncVar) + { + Objects.ParseSyncType(reader, false, args.Channel); + } + else if (packetId == PacketId.SyncObject) + { + Objects.ParseSyncType(reader, true, args.Channel); + } + else if (packetId == PacketId.PredictedSpawnResult) + { + Objects.ParsePredictedSpawnResult(reader); + } + else if (packetId == PacketId.TimingUpdate) + { + NetworkManager.TimeManager.ParseTimingUpdate(); + } + else if (packetId == PacketId.OwnershipChange) + { + Objects.ParseOwnershipChange(reader); + } + else if (packetId == PacketId.Authenticated) + { + ParseAuthenticated(reader); + } + else if (packetId == PacketId.Disconnect) + { + reader.Clear(); + StopConnection(); + } + else + { + + NetworkManager.LogError($"Client received an unhandled PacketId of {(ushort)packetId}. Remaining data has been purged."); +#if UNITY_EDITOR || DEVELOPMENT_BUILD + _parseLogger.Print(NetworkManager); +#endif + return; + } + } + } + + /* Iterate cache when reader is emptied. + * This is incase the last packet received + * was a spawned, which wouldn't trigger + * the above iteration. There's no harm + * in doing this check multiple times as there's + * an exit early check. */ + Objects.IterateObjectCache(); + } +#if !UNITY_EDITOR && !DEVELOPMENT_BUILD + } + catch (Exception e) + { + if (NetworkManager.CanLog(LoggingType.Error)) + Debug.LogError($"Client encountered an error while parsing data for packetId {packetId}. Message: {e.Message}."); + } +#endif + } + + /// + /// Parses a PingPong packet. + /// + /// + private void ParsePingPong(PooledReader reader) + { + uint clientTick = reader.ReadUInt32(AutoPackType.Unpacked); + NetworkManager.TimeManager.ModifyPing(clientTick); + } + + /// + /// Parses a received connectionId. This is received before client receives connection state change. + /// + /// + private void ParseAuthenticated(PooledReader reader) + { + NetworkManager networkManager = NetworkManager; + int connectionId = reader.ReadNetworkConnectionId(); + //If only a client then make a new connection. + if (!networkManager.IsServer) + { + Clients.TryGetValueIL2CPP(connectionId, out Connection); + } + /* If also the server then use the servers connection + * for the connectionId. This is to resolve host problems + * where LocalConnection for client differs from the server Connection + * reference, which results in different field values. */ + else + { + if (networkManager.ServerManager.Clients.TryGetValueIL2CPP(connectionId, out NetworkConnection conn)) + { + Connection = conn; + } + else + { + networkManager.LogError($"Unable to lookup LocalConnection for {connectionId} as host."); + Connection = new NetworkConnection(networkManager, connectionId, false); + } + } + + //If predicted spawning is enabled also get reserved Ids. + if (NetworkManager.PredictionManager.GetAllowPredictedSpawning()) + { + byte count = reader.ReadByte(); + Queue q = Connection.PredictedObjectIds; + for (int i = 0; i < count; i++) + q.Enqueue(reader.ReadNetworkObjectId()); + } + + /* Set the TimeManager tick to lastReceivedTick. + * This still doesn't account for latency but + * it's the best we can do until the client gets + * a ping response. */ + networkManager.TimeManager.Tick = networkManager.TimeManager.LastPacketTick; + + //Mark as authenticated. + Connection.ConnectionAuthenticated(); + OnAuthenticated?.Invoke(); + /* Register scene objects for all scenes + * after being authenticated. This is done after + * authentication rather than when the connection + * is started because if also as server an online + * scene may already be loaded on server, but not + * for client. This means the sceneLoaded unity event + * won't fire, and since client isn't authenticated + * at the connection start phase objects won't be added. */ + Objects.RegisterAndDespawnSceneObjects(); + } + + } + +} diff --git a/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs.meta b/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs.meta new file mode 100644 index 0000000..3185de7 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aca43cf6f20e77c4f8fcc078fd85081f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Client/Editor.meta b/Assets/FishNet/Runtime/Managing/Client/Editor.meta new file mode 100644 index 0000000..bf04e9c --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 934151ddc3910094daef3552e81ecf24 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Client/Editor/ClientManagerEditor.cs b/Assets/FishNet/Runtime/Managing/Client/Editor/ClientManagerEditor.cs new file mode 100644 index 0000000..4208062 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/Editor/ClientManagerEditor.cs @@ -0,0 +1,46 @@ +#if UNITY_EDITOR +using UnityEditor; +using UnityEngine; + +namespace FishNet.Managing.Client.Editing +{ + + + [CustomEditor(typeof(ClientManager), true)] + [CanEditMultipleObjects] + public class ClientManagerEditor : Editor + { + private SerializedProperty _changeFrameRate; + private SerializedProperty _frameRate; + + protected virtual void OnEnable() + { + _changeFrameRate = serializedObject.FindProperty("_changeFrameRate"); + _frameRate = serializedObject.FindProperty("_frameRate"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + GUI.enabled = false; + EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour((ClientManager)target), typeof(ClientManager), false); + GUI.enabled = true; + + + EditorGUILayout.PropertyField(_changeFrameRate); + if (_changeFrameRate.boolValue) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_frameRate); + EditorGUI.indentLevel--; + } + + EditorGUILayout.Space(); + + serializedObject.ApplyModifiedProperties(); + } + + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Client/Editor/ClientManagerEditor.cs.meta b/Assets/FishNet/Runtime/Managing/Client/Editor/ClientManagerEditor.cs.meta new file mode 100644 index 0000000..404a1fd --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/Editor/ClientManagerEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a652d51a1efa8a442966e885e2736599 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Client/Object.meta b/Assets/FishNet/Runtime/Managing/Client/Object.meta new file mode 100644 index 0000000..0717957 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/Object.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cd638699438c5194ca93b15a5121d0a8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs new file mode 100644 index 0000000..46a65d8 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs @@ -0,0 +1,87 @@ +using FishNet.Managing.Logging; +using FishNet.Managing.Object; +using FishNet.Managing.Utility; +using FishNet.Object; +using FishNet.Object.Helping; +using FishNet.Serializing; +using FishNet.Transporting; +using FishNet.Utility.Extension; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Managing.Client +{ + /// + /// Handles objects and information about objects for the local client. See ManagedObjects for inherited options. + /// + public partial class ClientObjects : ManagedObjects + { + + #region Private. + /// + /// RPCLinks of currently spawned objects. + /// + private Dictionary _rpcLinks = new Dictionary(); + #endregion + + /// + /// Parses a received RPCLink. + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ParseRpcLink(PooledReader reader, ushort index, Channel channel) + { + int dataLength = Packets.GetPacketLength(ushort.MaxValue, reader, channel); + + //Link index isn't stored. + if (!_rpcLinks.TryGetValueIL2CPP(index, out RpcLink link)) + { + SkipDataLength(index, reader, dataLength); + return; + } + else + //Found NetworkObject for link. + if (Spawned.TryGetValueIL2CPP(link.ObjectId, out NetworkObject nob)) + { + NetworkBehaviour nb = nob.NetworkBehaviours[link.ComponentIndex]; + if (link.RpcType == RpcType.Target) + nb.OnTargetRpc(link.RpcHash, reader, channel); + else if (link.RpcType == RpcType.Observers) + nb.OnObserversRpc(link.RpcHash, reader, channel); + else if (link.RpcType == RpcType.Reconcile) + nb.OnReconcileRpc(link.RpcHash, reader, channel); + } + //Could not find NetworkObject. + else + { + SkipDataLength(index, reader, dataLength, link.ObjectId); + } + } + + /// + /// Sets link to rpcLinks key linkIndex. + /// + /// + /// + internal void SetRpcLink(ushort linkIndex, RpcLink link) + { + _rpcLinks[linkIndex] = link; + } + + /// + /// Removes link index keys from rpcLinks. + /// + internal void RemoveLinkIndexes(List values) + { + if (values == null) + return; + + for (int i = 0; i < values.Count; i++) + _rpcLinks.Remove(values[i]); + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs.meta b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs.meta new file mode 100644 index 0000000..f129249 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2a20cc3f399aa614c931c9b45205937b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs new file mode 100644 index 0000000..ff177f4 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs @@ -0,0 +1,688 @@ +using FishNet.Connection; +using FishNet.Documenting; +using FishNet.Managing.Logging; +using FishNet.Managing.Object; +using FishNet.Managing.Server; +using FishNet.Managing.Utility; +using FishNet.Object; +using FishNet.Object.Helping; +using FishNet.Serializing; +using FishNet.Transporting; +using FishNet.Utility.Extension; +using FishNet.Utility.Performance; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace FishNet.Managing.Client +{ + /// + /// Handles objects and information about objects for the local client. See ManagedObjects for inherited options. + /// + public partial class ClientObjects : ManagedObjects + { + #region Private. + /// + /// NetworkObjects which are cached to be spawned or despawned. + /// + private ClientObjectCache _objectCache; + #endregion + + internal ClientObjects(NetworkManager networkManager) + { + base.Initialize(networkManager); + _objectCache = new ClientObjectCache(this, networkManager); + } + + /// + /// Called when a connection state changes for the local server. + /// + internal void OnServerConnectionState(ServerConnectionStateArgs args) + { + //Nothing needs to be done if started. + if (args.ConnectionState == LocalConnectionState.Started) + return; + + /* If not started and client is active then deinitialize + * client objects first. This will let the deinit calls + * perform before the server destroys them. Ideally this + * would be done when the user shows intent to shutdown + * the server, but realistically planning for server socket + * drops is a much more universal solution. + * + * Calling StopConnection on the client will set it's local state + * to Stopping which will result in a deinit. */ + if (NetworkManager.IsClient) + base.NetworkManager.ClientManager.StopConnection(); + } + + /// + /// Called when the connection state changes for the local client. + /// + /// + internal void OnClientConnectionState(ClientConnectionStateArgs args) + { + /* If new state is not started then reset + * environment. */ + if (args.ConnectionState != LocalConnectionState.Started) + { + _objectCache.Reset(); + + //If not server then deinitialize normally. + if (!base.NetworkManager.IsServer) + { + base.DespawnWithoutSynchronization(false); + } + //Otherwise invoke stop callbacks only for client side. + else + { + foreach (NetworkObject n in Spawned.Values) + n.InvokeStopCallbacks(false); + } + /* Clear spawned and scene objects as they will be rebuilt. + * Spawned would have already be cleared if DespawnSpawned + * was called but it won't hurt anything clearing an empty collection. */ + base.Spawned.Clear(); + base.SceneObjects.Clear(); + base.LocalClientSpawned.Clear(); + } + } + + + /// + /// Called when a scene is loaded. + /// + /// + /// + [APIExclude] + protected internal override void SceneManager_sceneLoaded(Scene s, LoadSceneMode arg1) + { + base.SceneManager_sceneLoaded(s, arg1); + + if (!base.NetworkManager.IsClient) + return; + /* When a scene first loads for a client it should disable + * all network objects in that scene. The server will send + * spawn messages once it's aware client has loaded the scene. */ + RegisterAndDespawnSceneObjects(s); + } + + + /// + /// Sends a predicted spawn to the server. + /// + internal void PredictedSpawn(NetworkObject networkObject, NetworkConnection ownerConnection) + { + //No more Ids to use. + Queue predictedObjectIds = NetworkManager.ClientManager.Connection.PredictedObjectIds; + if (predictedObjectIds.Count == 0) + { + NetworkManager.LogError($"Predicted spawn for object {networkObject.name} failed because no more predicted ObjectIds remain. This usually occurs when the client is spawning excessively before the server can respond. Increasing ReservedObjectIds within the ServerManager component or reducing spawn rate could prevent this problem."); + return; + } + + networkObject.PreinitializePredictedObject_Client(base.NetworkManager, predictedObjectIds.Dequeue(), ownerConnection, base.NetworkManager.ClientManager.Connection); + NetworkManager.ClientManager.Objects.AddToSpawned(networkObject, false); + networkObject.Initialize(false, true); + + PooledWriter writer = WriterPool.GetWriter(); + WriteSpawn(networkObject, writer); + base.NetworkManager.TransportManager.SendToServer((byte)Channel.Reliable, writer.GetArraySegment()); + writer.Dispose(); + } + + /// + /// Writes a predicted spawn. + /// + /// + public void WriteSpawn(NetworkObject nob, Writer writer) + { + PooledWriter headerWriter = WriterPool.GetWriter(); + headerWriter.WritePacketId(PacketId.ObjectSpawn); + headerWriter.WriteNetworkObjectForSpawn(nob); + headerWriter.WriteNetworkConnection(nob.Owner); + + bool sceneObject = nob.IsSceneObject; + //Write type of spawn. + SpawnType st = SpawnType.Unset; + if (sceneObject) + st |= SpawnType.Scene; + else + st |= (nob.IsGlobal) ? SpawnType.InstantiatedGlobal : SpawnType.Instantiated; + headerWriter.WriteByte((byte)st); + + //ComponentIndex for the nob. 0 is root but more appropriately there's a IsNested boolean as shown above. + headerWriter.WriteByte(nob.ComponentIndex); + //Properties on the transform which diff from serialized value. + base.WriteChangedTransformProperties(nob, sceneObject, false, headerWriter); + /* Writing a scene object. */ + if (sceneObject) + { + headerWriter.WriteUInt64(nob.SceneId, AutoPackType.Unpacked); + } + /* Writing a spawned object. */ + else + { + //Nested predicted spawning will be added later. + headerWriter.WriteByte((byte)SpawnParentType.Unset); + headerWriter.WriteNetworkObjectId(nob.PrefabId); + } + + writer.WriteBytes(headerWriter.GetBuffer(), 0, headerWriter.Length); + + //If allowed to write synctypes. + if (nob.AllowPredictedSyncTypes) + { + PooledWriter tempWriter = WriterPool.GetWriter(); + WriteSyncTypes(writer, tempWriter, SyncTypeWriteType.All); + void WriteSyncTypes(Writer finalWriter, PooledWriter tWriter, SyncTypeWriteType writeType) + { + tWriter.Reset(); + foreach (NetworkBehaviour nb in nob.NetworkBehaviours) + nb.WriteSyncTypesForSpawn(tWriter, writeType); + finalWriter.WriteBytesAndSize(tWriter.GetBuffer(), 0, tWriter.Length); + } + tempWriter.Dispose(); + } + + //Dispose of writers created in this method. + headerWriter.Dispose(); + } + + + /// + /// Sends a predicted despawn to the server. + /// + internal void PredictedDespawn(NetworkObject networkObject) + { + PooledWriter writer = WriterPool.GetWriter(); + WriteDepawn(networkObject, writer); + base.NetworkManager.TransportManager.SendToServer((byte)Channel.Reliable, writer.GetArraySegment()); + writer.Dispose(); + + //Deinitialize after writing despawn so all the right data is sent. + networkObject.DeinitializePredictedObject_Client(); + } + + /// + /// Writes a predicted despawn. + /// + public void WriteDepawn(NetworkObject nob, Writer writer) + { + writer.WritePacketId(PacketId.ObjectDespawn); + writer.WriteNetworkObject(nob); + } + + /// + /// Registers NetworkObjects in all scenes and despawns them. + /// + internal void RegisterAndDespawnSceneObjects() + { + for (int i = 0; i < SceneManager.sceneCount; i++) + RegisterAndDespawnSceneObjects(SceneManager.GetSceneAt(i)); + } + + /// + /// Adds NetworkObjects within s to SceneObjects, and despawns them. + /// + /// + private void RegisterAndDespawnSceneObjects(Scene s) + { + ListCache nobs; + SceneFN.GetSceneNetworkObjects(s, false, out nobs); + + for (int i = 0; i < nobs.Written; i++) + { + NetworkObject nob = nobs.Collection[i]; + if (!nob.IsSceneObject) + continue; + + base.UpdateNetworkBehavioursForSceneObject(nob, false); + if (nob.IsNetworked && nob.IsNetworked) + { + base.AddToSceneObjects(nob); + //Only run if not also server, as this already ran on server. + if (!base.NetworkManager.IsServer) + nob.gameObject.SetActive(false); + } + } + + ListCaches.StoreCache(nobs); + } + + /// + /// Called when a NetworkObject runs Deactivate. + /// + /// + internal override void NetworkObjectUnexpectedlyDestroyed(NetworkObject nob, bool asServer) + { + nob.RemoveClientRpcLinkIndexes(); + base.NetworkObjectUnexpectedlyDestroyed(nob, asServer); + } + + /// + /// Parses an OwnershipChange packet. + /// + /// + internal void ParseOwnershipChange(PooledReader reader) + { + NetworkObject nob = reader.ReadNetworkObject(); + NetworkConnection newOwner = reader.ReadNetworkConnection(); + if (nob != null) + nob.GiveOwnership(newOwner, false); + else + NetworkManager.LogWarning($"NetworkBehaviour could not be found when trying to parse OwnershipChange packet."); + } + + /// + /// Parses a received syncVar. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ParseSyncType(PooledReader reader, bool isSyncObject, Channel channel) + { + //cleanup this is unique to synctypes where length comes first. + //this will change once I tidy up synctypes. + ushort packetId = (isSyncObject) ? (ushort)PacketId.SyncObject : (ushort)PacketId.SyncVar; + NetworkBehaviour nb = reader.ReadNetworkBehaviour(); + int dataLength = Packets.GetPacketLength(packetId, reader, channel); + + if (nb != null) + { + /* Length of data to be read for syncvars. + * This is important because syncvars are never + * a set length and data must be read through completion. + * The only way to know where completion of syncvar is, versus + * when another packet starts is by including the length. */ + if (dataLength > 0) + nb.OnSyncType(reader, dataLength, isSyncObject); + } + else + { + SkipDataLength(packetId, reader, dataLength); + } + } + + /// + /// Parses a + /// + /// + internal void ParsePredictedSpawnResult(Reader reader) + { + int usedObjectId = reader.ReadNetworkObjectId(); + bool success = reader.ReadBoolean(); + if (success) + { + int nextObjectId = reader.ReadNetworkObjectId(); + if (nextObjectId != NetworkObject.UNSET_OBJECTID_VALUE) + NetworkManager.ClientManager.Connection.PredictedObjectIds.Enqueue(nextObjectId); + } + } + + /// + /// Parses a ReconcileRpc. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ParseReconcileRpc(PooledReader reader, Channel channel) + { + NetworkBehaviour nb = reader.ReadNetworkBehaviour(); + int dataLength = Packets.GetPacketLength((ushort)PacketId.Reconcile, reader, channel); + + if (nb != null) + nb.OnReconcileRpc(null, reader, channel); + else + SkipDataLength((ushort)PacketId.ObserversRpc, reader, dataLength); + } + + /// + /// Parses an ObserversRpc. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ParseObserversRpc(PooledReader reader, Channel channel) + { + NetworkBehaviour nb = reader.ReadNetworkBehaviour(); + int dataLength = Packets.GetPacketLength((ushort)PacketId.ObserversRpc, reader, channel); + + if (nb != null) + nb.OnObserversRpc(null, reader, channel); + else + SkipDataLength((ushort)PacketId.ObserversRpc, reader, dataLength); + } + /// + /// Parses a TargetRpc. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ParseTargetRpc(PooledReader reader, Channel channel) + { + NetworkBehaviour nb = reader.ReadNetworkBehaviour(); + int dataLength = Packets.GetPacketLength((ushort)PacketId.TargetRpc, reader, channel); + + if (nb != null) + nb.OnTargetRpc(null, reader, channel); + else + SkipDataLength((ushort)PacketId.TargetRpc, reader, dataLength); + } + + /// + /// Caches a received spawn to be processed after all spawns and despawns are received for the tick. + /// + /// + internal void CacheSpawn(PooledReader reader) + { + sbyte initializeOrder; + ushort collectionId; + int objectId = reader.ReadNetworkObjectForSpawn(out initializeOrder, out collectionId, out _); + int ownerId = reader.ReadNetworkConnectionId(); + SpawnType st = (SpawnType)reader.ReadByte(); + byte componentIndex = reader.ReadByte(); + + //Read transform values which differ from serialized values. + Vector3? localPosition; + Quaternion? localRotation; + Vector3? localScale; + base.ReadTransformProperties(reader, out localPosition, out localRotation, out localScale); + + bool nested = SpawnTypeEnum.Contains(st, SpawnType.Nested); + int rootObjectId = (nested) ? reader.ReadNetworkObjectId() : 0; + bool sceneObject = SpawnTypeEnum.Contains(st, SpawnType.Scene); + + int? parentObjectId = null; + byte? parentComponentIndex = null; + int? prefabId = null; + ulong sceneId = 0; + + if (sceneObject) + ReadSceneObject(reader, out sceneId); + else + ReadSpawnedObject(reader, out parentObjectId, out parentComponentIndex, out prefabId); + + ArraySegment rpcLinks = reader.ReadArraySegmentAndSize(); + ArraySegment syncValues = reader.ReadArraySegmentAndSize(); + + /* If the objectId can be found as already spawned then check if it's predicted. + * Should the spawn be predicted then no need to continue. Later on however + * we may want to apply synctypes. + * + * Only check if not server, since if server the client doesnt need + * to predicted spawn. */ + if (!base.NetworkManager.IsServerOnly && base.Spawned.TryGetValue(objectId, out NetworkObject nob)) + { + //If not predicted the nob should not be in spawned. + if (!nob.PredictedSpawner.IsValid) + { + NetworkManager.LogError($"Received a spawn objectId of {objectId} which was already found in spawned, and was not predicted."); + } + //Everything is proper, apply RPC links. + else + { + PooledReader linkReader = ReaderPool.GetReader(rpcLinks, NetworkManager); + ApplyRpcLinks(nob, linkReader); + linkReader.Dispose(); + } + //No further initialization needed when predicting. + return; + } + + _objectCache.AddSpawn(base.NetworkManager, collectionId, objectId, initializeOrder, ownerId, st, componentIndex, rootObjectId, parentObjectId, parentComponentIndex, prefabId, localPosition, localRotation, localScale, sceneId, rpcLinks, syncValues); + } + /// + /// Caches a received despawn to be processed after all spawns and despawns are received for the tick. + /// + /// + internal void CacheDespawn(PooledReader reader) + { + DespawnType despawnType; + int objectId = reader.ReadNetworkObjectForDepawn(out despawnType); + _objectCache.AddDespawn(objectId, despawnType); + } + + /// + /// Iterates object cache which contains spawn and despawn messages. + /// Parses the packets within the cache and ensures objects are spawned and despawned before their sync values are applied. + /// This ensures there is no chance a sync value is referencing a spawned object which does not exist yet due to it normally being spawned later in the cache. + /// + internal void IterateObjectCache() + { + _objectCache.Iterate(); + } + + /// + /// Gets a nested NetworkObject within it's root. + /// + /// + /// + internal NetworkObject GetNestedNetworkObject(CachedNetworkObject cnob) + { + NetworkObject rootNob; + int rootObjectId = cnob.RootObjectId; + byte componentIndex = cnob.ComponentIndex; + + /* Spawns are processed after all spawns come in, + * this ensures no reference race conditions. Turns out because of this + * the parentNob may be in cache and not actually spawned, if it was spawned the same packet + * as this one. So when not found in the spawned collection try to + * find it in Spawning before throwing. */ + rootNob = _objectCache.GetSpawnedObject(rootObjectId); + //If still null, that's not good. + if (rootNob == null) + { + NetworkManager.LogError($"Nested spawned object with componentIndex of {componentIndex} and a parentId of {rootObjectId} could not be spawned because parent was not found."); + return null; + } + + NetworkObject nob = null; + List childNobs = rootNob.ChildNetworkObjects; + //Find nob with component index. + for (int i = 0; i < childNobs.Count; i++) + { + if (childNobs[i].ComponentIndex == componentIndex) + { + nob = childNobs[i]; + break; + } + } + //If child nob was not found. + if (nob == null) + { + NetworkManager.LogError($"Nested spawned object with componentIndex of {componentIndex} could not be found as a child NetworkObject of {rootNob.name}."); + return null; + } + + return nob; + } + + /// + /// Applies RPCLinks to a NetworkObject. + /// + internal void ApplyRpcLinks(NetworkObject nob, Reader reader) + { + List rpcLinkIndexes = new List(); + //Apply rpcLinks. + foreach (NetworkBehaviour nb in nob.NetworkBehaviours) + { + int length = reader.ReadInt32(); + + int readerStart = reader.Position; + while (reader.Position - readerStart < length) + { + //Index of RpcLink. + ushort linkIndex = reader.ReadUInt16(); + RpcLink link = new RpcLink(nob.ObjectId, nb.ComponentIndex, + //RpcHash. + reader.ReadUInt16(), + //ObserverRpc. + (RpcType)reader.ReadByte()); + //Add to links. + SetRpcLink(linkIndex, link); + + rpcLinkIndexes.Add(linkIndex); + } + } + nob.SetRpcLinkIndexes(rpcLinkIndexes); + } + + /// + /// Instantiates a NetworkObject if required and sets transform values. + /// + internal NetworkObject GetInstantiatedNetworkObject(CachedNetworkObject cnob) + { + if (cnob.PrefabId == null) + { + NetworkManager.LogError($"PrefabId for {cnob.ObjectId} is null. Object will not spawn."); + return null; + } + + NetworkManager networkManager = base.NetworkManager; + int prefabId = cnob.PrefabId.Value; + NetworkObject result; + + if (prefabId == NetworkObject.UNSET_OBJECTID_VALUE) + { + NetworkManager.LogError($"Spawned object has an invalid prefabId. Make sure all objects which are being spawned over the network are within SpawnableObjects on the NetworkManager."); + return null; + } + + ushort collectionId = cnob.CollectionId; + //PrefabObjects to get the prefab from. + PrefabObjects prefabObjects = networkManager.GetPrefabObjects(collectionId, false); + //Not found for collectionId > 0. This means the user likely did not setup the collection on client. + if (prefabObjects == null && collectionId > 0) + { + networkManager.LogError($"PrefabObjects collection is not found for CollectionId {collectionId}. Be sure to add your addressables NetworkObject prefabs to the collection on server and client before attempting to spawn them over the network."); + return null; + } + + //Only instantiate if not host. + if (!networkManager.IsHost) + { + Transform parentTransform = null; + bool hasParent = (cnob.ParentObjectId != null); + //Set parentTransform if there's a parent object. + if (hasParent) + { + int objectId = cnob.ParentObjectId.Value; + NetworkObject nob = _objectCache.GetSpawnedObject(objectId); + + if (nob == null) + { + NetworkObject prefab = prefabObjects.GetObject(false, prefabId); + networkManager.LogError($"NetworkObject not found for ObjectId {objectId}. Prefab {prefab.name} will be instantiated without parent synchronization."); + } + else + { + //If parent object is a network behaviour then find the component. + if (cnob.ParentIsNetworkBehaviour) + { + byte componentIndex = cnob.ComponentIndex; + NetworkBehaviour nb = nob.GetNetworkBehaviour(componentIndex, false); + if (nb != null) + { + parentTransform = nb.transform; + } + else + { + NetworkObject prefab = prefabObjects.GetObject(false, prefabId); + networkManager.LogError($"NetworkBehaviour on index {componentIndex} could nto be found within NetworkObject {nob.name} with ObjectId {objectId}. Prefab {prefab.name} will be instantiated without parent synchronization."); + } + } + //The networkObject is the parent. + else + { + parentTransform = nob.transform; + } + } + } + + result = networkManager.GetPooledInstantiated(prefabId, collectionId, false); + Transform t = result.transform; + t.SetParent(parentTransform, true); + //Only need to set IsGlobal also if not host. + bool isGlobal = SpawnTypeEnum.Contains(cnob.SpawnType, SpawnType.InstantiatedGlobal); + result.SetIsGlobal(isGlobal); + } + //If host then find server instantiated object. + else + { + ServerObjects so = networkManager.ServerManager.Objects; + if (!so.Spawned.TryGetValueIL2CPP(cnob.ObjectId, out result)) + result = so.GetFromPending(cnob.ObjectId); + + if (result == null) + networkManager.LogError($"ObjectId {cnob.ObjectId} could not be found in Server spawned, nor Server pending despawn."); + } + + return result; + } + + /// + /// Gets a NetworkObject from Spawned, or object cache. + /// + /// + /// + internal NetworkObject GetSpawnedNetworkObject(CachedNetworkObject cnob) + { + NetworkObject nob; + //Try checking already spawned objects first. + if (base.Spawned.TryGetValueIL2CPP(cnob.ObjectId, out nob)) + { + return nob; + } + /* If not found in already spawned objects see if + * the networkObject is in the objectCache. It's possible the despawn + * came immediately or shortly after the spawn message, before + * the object has been initialized. */ + else + { + nob = _objectCache.GetInCached(cnob.ObjectId, ClientObjectCache.CacheSearchType.Any); + /* Nob may be null if it's a child object being despawned, and the + * parent despawn already occurred. */ + return nob; + } + } + + /// + /// Finishes reading a scene object. + /// + private void ReadSceneObject(PooledReader reader, out ulong sceneId) + { + sceneId = reader.ReadUInt64(AutoPackType.Unpacked); + } + + /// + /// Finishes reading a spawned object, and instantiates the object. + /// + private void ReadSpawnedObject(PooledReader reader, out int? parentObjectId, out byte? parentComponentIndex, out int? prefabId) + { + //Parent. + SpawnParentType spt = (SpawnParentType)reader.ReadByte(); + //Defaults. + parentObjectId = null; + parentComponentIndex = null; + + if (spt == SpawnParentType.NetworkObject) + { + int objectId = reader.ReadNetworkObjectId(); + if (objectId != NetworkObject.UNSET_OBJECTID_VALUE) + parentObjectId = objectId; + } + else if (spt == SpawnParentType.NetworkBehaviour) + { + reader.ReadNetworkBehaviour(out int objectId, out byte componentIndex); + if (objectId != NetworkObject.UNSET_OBJECTID_VALUE) + { + parentObjectId = objectId; + parentComponentIndex = componentIndex; + } + } + + prefabId = (ushort)reader.ReadNetworkObjectId(); + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs.meta b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs.meta new file mode 100644 index 0000000..014b78e --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: da027fa27b0c0994ebfa317968862970 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Client/Object/ObjectCaching.cs b/Assets/FishNet/Runtime/Managing/Client/Object/ObjectCaching.cs new file mode 100644 index 0000000..e86387a --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/Object/ObjectCaching.cs @@ -0,0 +1,605 @@ +using FishNet.Connection; +using FishNet.Managing.Logging; +using FishNet.Managing.Object; +using FishNet.Object; +using FishNet.Object.Helping; +using FishNet.Serializing; +using FishNet.Utility.Extension; +using FishNet.Utility.Performance; +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Scripting; + +namespace FishNet.Managing.Client +{ + + /// + /// Information about cached network objects. + /// + internal class ClientObjectCache + { + #region Types. + public enum CacheSearchType + { + Any = 0, + Spawning = 1, + Despawning = 2 + } + #endregion + + #region Internal. + /// + /// Objets which are being spawned during iteration. + /// + internal Dictionary SpawningObjects = new Dictionary(); + #endregion + + #region Private. + /// + /// Cached objects buffer. Contains spawns and despawns. + /// + private ListCache _cachedObjects = new ListCache(); + /// + /// NetworkObjects which have been spawned already during the current iteration. + /// + private HashSet _iteratedSpawns = new HashSet(); + /// + /// Despawns which are occurring the same tick as their spawn. + /// + private HashSet _conflictingDespawns = new HashSet(); + /// + /// ClientObjects reference. + /// + private ClientObjects _clientObjects; + /// + /// NetworkManager for this cache. + /// + private NetworkManager _networkManager; + /// + /// True if logged the warning about despawning on the same tick as the spawn. + /// This exist to prevent excessive spam of the warning. + /// + private bool _loggedSameTickWarning; + /// + /// True if initializeOrder was not default for any spawned objects. + /// + private bool _initializeOrderChanged; + #endregion + + public ClientObjectCache(ClientObjects cobs, NetworkManager networkManager) + { + _clientObjects = cobs; + _networkManager = networkManager; + } + + /// + /// Returns a NetworkObject found in spawned cache using objectId. + /// + /// + /// + public NetworkObject GetInCached(int objectId, CacheSearchType searchType) + { + int count = _cachedObjects.Written; + List collection = _cachedObjects.Collection; + for (int i = 0; i < count; i++) + { + CachedNetworkObject cnob = collection[i]; + if (cnob.ObjectId == objectId) + { + //Any condition always returns. + if (searchType == CacheSearchType.Any) + return cnob.NetworkObject; + + bool spawning = (searchType == CacheSearchType.Spawning); + bool spawnAction = (cnob.Action == CachedNetworkObject.ActionType.Spawn); + if (spawning == spawnAction) + return cnob.NetworkObject; + else + return null; + } + } + + //Fall through. + return null; + } + + /// + /// Initializes for a spawned NetworkObject. + /// + /// + /// + /// + public void AddSpawn(NetworkManager manager, ushort collectionId, int objectId, sbyte initializeOrder, int ownerId, SpawnType ost, byte componentIndex, int rootObjectId, int? parentObjectId, byte? parentComponentIndex + , int? prefabId, Vector3? localPosition, Quaternion? localRotation, Vector3? localScale, ulong sceneId, ArraySegment rpcLinks, ArraySegment syncValues) + { + //Set if initialization order has changed. + _initializeOrderChanged |= (initializeOrder != 0); + + CachedNetworkObject cnob = null; + //If order has not changed then add normally. + if (!_initializeOrderChanged) + { + cnob = _cachedObjects.AddReference(); + } + //Otherwise see if values need to be sorted. + else + { + /* Spawns will be ordered at the end of their nearest order. + * If spawns arrived with Id order of 5, 7, 2 then the result + * would be as shown below... + * Id 5 / order -5 + * Id 7 / order -5 + * Id 2 / order 0 + * Not as if the values were inserted first such as... + * Id 7 / order -5 + * Id 5 / order -5 + * Id 2 / order 0 + * This is to prevent the likeliness of child nobs being out of order + * as well to preserve user spawn order if they spawned multiple + * objects the same which, with the same order. */ + + int written = _cachedObjects.Written; + for (int i = 0; i < written; i++) + { + CachedNetworkObject item = _cachedObjects.Collection[i]; + /* If item order is larger then that means + * initializeOrder has reached the last entry + * of its value. Insert just before item index. */ + if (initializeOrder < item.InitializeOrder) + { + cnob = _cachedObjects.InsertReference(i); + break; + } + } + + //If here and cnob is null then it was not inserted; add to end. + if (cnob == null) + cnob = _cachedObjects.AddReference(); + } + + cnob.InitializeSpawn(manager, collectionId, objectId, initializeOrder, ownerId, ost, componentIndex, rootObjectId, parentObjectId, parentComponentIndex + , prefabId, localPosition, localRotation, localScale, sceneId, rpcLinks, syncValues); + } + + public void AddDespawn(int objectId, DespawnType despawnType) + { + CachedNetworkObject cnob = _cachedObjects.AddReference(); + cnob.InitializeDespawn(objectId, despawnType); + } + + /// + /// Iterates any written objects. + /// + public void Iterate() + { + int written = _cachedObjects.Written; + if (written == 0) + return; + + try + { + //Indexes which have already been processed. + HashSet processedIndexes = new HashSet(); + List collection = _cachedObjects.Collection; + _conflictingDespawns.Clear(); + /* The next iteration will set rpclinks, + * synctypes, and so on. */ + for (int i = 0; i < written; i++) + { + /* An index may already be processed if it was pushed ahead. + * This can occur if a nested object spawn exists but the root + * object has not spawned yet. In this situation the root spawn is + * found and performed first. */ + if (processedIndexes.Contains(i)) + continue; + CachedNetworkObject cnob = collection[i]; + bool spawn = (cnob.Action == CachedNetworkObject.ActionType.Spawn); + + /* See if nested, and if so check if root is already spawned. + * If parent is not spawned then find it and process the parent first. */ + if (spawn) + { + /* When an object is nested or has a parent it is + * dependent upon either the root of nested, or the parent, + * being spawned to setup properly. + * + * When either of these are true check spawned objects first + * to see if the objects exist. If not check if they are appearing + * later in the cache. Root or parent objects can appear later + * in the cache depending on the order of which observers are rebuilt. + * While it is possible to have the server ensure spawns always send + * root/parents first, that's a giant can of worms that's not worth getting into. + * Not only are there many scenarios to cover, but it also puts more work + * on the server. It's more effective to have the client handle the sorting. */ + + //Nested. + if (cnob.IsNested || cnob.HasParent) + { + bool nested = cnob.IsNested; + //It's not possible to be nested and have a parent. Set the Id to look for based on if nested or parented. + int targetObjectId = (nested) ? cnob.RootObjectId : cnob.ParentObjectId.Value; + NetworkObject nob = GetSpawnedObject(targetObjectId); + //If not spawned yet. + if (nob == null) + { + bool found = false; + string errMsg; + for (int z = (i + 1); z < written; z++) + { + CachedNetworkObject zCnob = collection[z]; + if (zCnob.ObjectId == targetObjectId) + { + found = true; + if (cnob.Action != CachedNetworkObject.ActionType.Spawn) + { + errMsg = (nested) + ? $"ObjectId {targetObjectId} was found for a nested spawn, but ActionType is not spawn. ComponentIndex {cnob.ComponentIndex} will not be spawned." + : $"ObjectId {targetObjectId} was found for a parented spawn, but ActionType is not spawn. ObjectId {cnob.ObjectId} will not be spawned."; + _networkManager.LogError(errMsg); + break; + } + else + { + ProcessObject(zCnob, true, z); + break; + } + } + } + + //Root nob could not be found. + if (!found) + { + errMsg = (nested) + ? $"ObjectId {targetObjectId} could not be found for a nested spawn. ComponentIndex {cnob.ComponentIndex} will not be spawned." + : $"ObjectId {targetObjectId} was found for a parented spawn. ObjectId {cnob.ObjectId} will not be spawned."; + _networkManager.LogError(errMsg); + } + } + } + } + + ProcessObject(cnob, spawn, i); + } + + void ProcessObject(CachedNetworkObject cnob, bool spawn, int index) + { + processedIndexes.Add(index); + + if (spawn) + { + if (cnob.IsSceneObject) + cnob.NetworkObject = _clientObjects.GetSceneNetworkObject(cnob.SceneId); + else if (cnob.IsNested) + cnob.NetworkObject = _clientObjects.GetNestedNetworkObject(cnob); + else + cnob.NetworkObject = _clientObjects.GetInstantiatedNetworkObject(cnob); + + /* Apply transform changes but only if not host. + * These would have already been applied server side. */ + if (!_networkManager.IsHost && cnob.NetworkObject != null) + { + Transform t = cnob.NetworkObject.transform; + _clientObjects.GetTransformProperties(cnob.LocalPosition, cnob.LocalRotation, cnob.LocalScale, t, out Vector3 pos, out Quaternion rot, out Vector3 scale); + t.SetLocalPositionRotationAndScale(pos, rot, scale); + } + } + else + { + cnob.NetworkObject = _clientObjects.GetSpawnedNetworkObject(cnob); + /* Do not log unless not nested. Nested nobs sometimes + * could be destroyed if parent was first. */ + if (!_networkManager.IsHost && cnob.NetworkObject == null && !cnob.IsNested) + _networkManager.Log($"NetworkObject for ObjectId of {cnob.ObjectId} was found null. Unable to despawn object. This may occur if a nested NetworkObject had it's parent object unexpectedly destroyed. This incident is often safe to ignore."); + } + NetworkObject nob = cnob.NetworkObject; + //No need to error here, the other Gets above would have. + if (nob == null) + return; + + if (spawn) + { + //If not also server then object also has to be preinitialized. + if (!_networkManager.IsServer) + { + int ownerId = cnob.OwnerId; + //If local client is owner then use localconnection reference. + NetworkConnection localConnection = _networkManager.ClientManager.Connection; + NetworkConnection owner; + //If owner is self. + if (ownerId == localConnection.ClientId) + { + owner = localConnection; + } + else + { + /* If owner cannot be found then share owners + * is disabled */ + if (!_networkManager.ClientManager.Clients.TryGetValueIL2CPP(ownerId, out owner)) + owner = NetworkManager.EmptyConnection; + } + nob.Preinitialize_Internal(_networkManager, cnob.ObjectId, owner, false); + } + + _clientObjects.AddToSpawned(cnob.NetworkObject, false); + SpawningObjects.Add(cnob.ObjectId, cnob.NetworkObject); + + IterateSpawn(cnob); + _iteratedSpawns.Add(cnob.NetworkObject); + + /* Enable networkObject here if client only. + * This is to ensure Awake fires in the same order + * as InitializeOrder settings. There is no need + * to perform this action if server because server + * would have already spawned in order. */ + if (!_networkManager.IsServer && cnob.NetworkObject != null) + cnob.NetworkObject.gameObject.SetActive(true); + } + else + { + /* If spawned already this iteration then the nob + * must be initialized so that the start/stop cycles + * complete normally. Otherwise, the despawn callbacks will + * fire immediately while the start callbacks will run after all + * spawns have been iterated. + * The downside to this is that synctypes + * for spawns later in this iteration will not be initialized + * yet, and if the nob being spawned/despawned references + * those synctypes the values will be default. + * + * The alternative is to delay the despawning until after + * all spawns are iterated, but that will break the order + * reliability. This is unfortunately a lose/lose situation so + * the best we can do is let the user know the risk. */ + if (_iteratedSpawns.Contains(cnob.NetworkObject)) + { + if (!_loggedSameTickWarning) + { + _loggedSameTickWarning = true; + _networkManager.LogWarning($"NetworkObject {cnob.NetworkObject.name} is being despawned on the same tick it's spawned." + + $" When this occurs SyncTypes will not be set on other objects during the time of this despawn." + + $" In result, if NetworkObject {cnob.NetworkObject.name} is referencing a SyncType of another object being spawned this tick, the returned values will be default."); + } + + _conflictingDespawns.Add(cnob.ObjectId); + cnob.NetworkObject.gameObject.SetActive(true); + cnob.NetworkObject.Initialize(false, true); + } + //Now being initialized, despawn the object. + IterateDespawn(cnob); + } + } + + /* Activate the objects after all data + * has been synchronized. This will apply synctypes. */ + for (int i = 0; i < written; i++) + { + CachedNetworkObject cnob = collection[i]; + if (cnob.Action == CachedNetworkObject.ActionType.Spawn && cnob.NetworkObject != null) + { + /* Apply syncTypes. It's very important to do this after all + * spawns have been processed and added to the manager.Objects collection. + * Otherwise, the synctype may reference an object spawning the same tick + * and the result would be null due to said object not being in spawned. + * + * At this time the NetworkObject is not initialized so by calling + * OnSyncType the changes are cached to invoke callbacks after initialization, + * not during the time of this action. */ + foreach (NetworkBehaviour nb in cnob.NetworkObject.NetworkBehaviours) + { + PooledReader reader = cnob.SyncValuesReader; + //SyncVars. + int length = reader.ReadInt32(); + nb.OnSyncType(reader, length, false); + //SyncObjects + length = reader.ReadInt32(); + nb.OnSyncType(reader, length, true); + } + + /* Only continue with the initialization if it wasn't initialized + * early to prevent a despawn conflict. */ + bool canInitialize = (!_conflictingDespawns.Contains(cnob.ObjectId) || !_iteratedSpawns.Contains(cnob.NetworkObject)); + if (canInitialize) + cnob.NetworkObject.Initialize(false, false); + } + } + //Invoke synctype callbacks. + for (int i = 0; i < written; i++) + { + CachedNetworkObject cnob = collection[i]; + if (cnob.Action == CachedNetworkObject.ActionType.Spawn && cnob.NetworkObject != null) + cnob.NetworkObject.InvokeSyncTypeCallbacks(false); + } + } + finally + { + //Once all have been iterated reset. + Reset(); + } + } + + /// + /// Initializes an object on clients and spawns the NetworkObject. + /// + /// + private void IterateSpawn(CachedNetworkObject cnob) + { + /* All nob spawns have been added to spawned before + * they are processed. This ensures they will be found if + * anything is referencing them before/after initialization. */ + /* However, they have to be added again here should an ItereteDespawn + * had removed them. This can occur if an object is set to be spawned, + * thus added to spawned before iterations, then a despawn runs which + * removes it from spawn. */ + _clientObjects.AddToSpawned(cnob.NetworkObject, false); + _clientObjects.ApplyRpcLinks(cnob.NetworkObject, cnob.RpcLinkReader); + } + + /// + /// Deinitializes an object on clients and despawns the NetworkObject. + /// + /// + private void IterateDespawn(CachedNetworkObject cnob) + { + _clientObjects.Despawn(cnob.NetworkObject, cnob.DespawnType, false); + } + + /// + /// Returns a NetworkObject found in spawn cache, or Spawned. + /// + /// + internal NetworkObject GetSpawnedObject(int objectId) + { + NetworkObject result; + //If not found in Spawning then check Spawned. + if (!SpawningObjects.TryGetValue(objectId, out result)) + { + Dictionary spawned = (_networkManager.IsHost) ? + _networkManager.ServerManager.Objects.Spawned + : _networkManager.ClientManager.Objects.Spawned; + spawned.TryGetValue(objectId, out result); + } + + return result; + } + + + /// + /// Resets cache. + /// + public void Reset() + { + _initializeOrderChanged = false; + _cachedObjects.Reset(); + _iteratedSpawns.Clear(); + SpawningObjects.Clear(); + } + } + + /// + /// A cached network object which exist in world but has not been Initialized yet. + /// + [Preserve] + internal class CachedNetworkObject + { + #region Types. + public enum ActionType + { + Unset = 0, + Spawn = 1, + Despawn = 2, + } + #endregion + + /// + /// True if cached object is nested. + /// + public bool IsNested => (ComponentIndex > 0); + /// + /// True if a scene object. + /// + public bool IsSceneObject => (SceneId > 0); + /// + /// True if this object has a parent. + /// + public bool HasParent => (ParentObjectId != null); + /// + /// True if the parent object is a NetworkBehaviour. + /// + public bool ParentIsNetworkBehaviour => (HasParent && (ParentComponentIndex != null)); + + public ushort CollectionId; + public int ObjectId; + public sbyte InitializeOrder; + public int OwnerId; + public SpawnType SpawnType; + public DespawnType DespawnType; + public byte ComponentIndex; + public int RootObjectId; + public int? ParentObjectId; + public byte? ParentComponentIndex; + public int? PrefabId; + public Vector3? LocalPosition; + public Quaternion? LocalRotation; + public Vector3? LocalScale; + public ulong SceneId; + public ArraySegment RpcLinks; + public ArraySegment SyncValues; + + + + /// + /// True if spawning. + /// + public ActionType Action { get; private set; } + /// + /// Cached NetworkObject. + /// +#pragma warning disable 0649 + public NetworkObject NetworkObject; + /// + /// Reader containing rpc links for the network object. + /// + public PooledReader RpcLinkReader { get; private set; } + /// + /// Reader containing sync values for the network object. + /// + public PooledReader SyncValuesReader { get; private set; } +#pragma warning restore 0649 + + public void InitializeSpawn(NetworkManager manager, ushort collectionId, int objectId, sbyte initializeOrder, int ownerId, SpawnType objectSpawnType, byte componentIndex, int rootObjectId, int? parentObjectId, byte? parentComponentIndex + , int? prefabId, Vector3? localPosition, Quaternion? localRotation, Vector3? localScale, ulong sceneId, ArraySegment rpcLinks, ArraySegment syncValues) + { + ResetValues(); + Action = ActionType.Spawn; + CollectionId = collectionId; + ObjectId = objectId; + InitializeOrder = initializeOrder; + OwnerId = ownerId; + SpawnType = objectSpawnType; + ComponentIndex = componentIndex; + RootObjectId = rootObjectId; + ParentObjectId = parentObjectId; + ParentComponentIndex = parentComponentIndex; + PrefabId = prefabId; + LocalPosition = localPosition; + LocalRotation = localRotation; + LocalScale = localScale; + SceneId = sceneId; + RpcLinks = rpcLinks; + SyncValues = syncValues; + + RpcLinkReader = ReaderPool.GetReader(rpcLinks, manager); + SyncValuesReader = ReaderPool.GetReader(syncValues, manager); + } + + /// + /// Initializes for a despawned NetworkObject. + /// + /// + public void InitializeDespawn(int objectId, DespawnType despawnType) + { + ResetValues(); + Action = ActionType.Despawn; + DespawnType = despawnType; + ObjectId = objectId; + } + + /// + /// Resets values which could malform identify the cached object. + /// + private void ResetValues() + { + NetworkObject = null; + } + + ~CachedNetworkObject() + { + if (RpcLinkReader != null) + RpcLinkReader.Dispose(); + if (SyncValuesReader != null) + SyncValuesReader.Dispose(); + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Client/Object/ObjectCaching.cs.meta b/Assets/FishNet/Runtime/Managing/Client/Object/ObjectCaching.cs.meta new file mode 100644 index 0000000..5481a01 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/Object/ObjectCaching.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 54cb2af8ab4557d479acb7fed98bd0c3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Debugging.meta b/Assets/FishNet/Runtime/Managing/Debugging.meta new file mode 100644 index 0000000..63f6644 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Debugging.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ae208c57eb1f4414896575607893b48d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Debugging/DebugManager.cs b/Assets/FishNet/Runtime/Managing/Debugging/DebugManager.cs new file mode 100644 index 0000000..777b5ef --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Debugging/DebugManager.cs @@ -0,0 +1,20 @@ +using UnityEngine; + +namespace FishNet.Managing.Debugging +{ + /// + /// A container for debugging. + /// + [DisallowMultipleComponent] + [AddComponentMenu("FishNet/Manager/DebugManager")] + public class DebugManager : MonoBehaviour + { + public bool ObserverRpcLinks = true; + public bool TargetRpcLinks = true; + public bool ReplicateRpcLinks = true; + public bool ReconcileRpcLinks = true; + public bool ServerRpcLinks = true; + } + + +} diff --git a/Assets/FishNet/Runtime/Managing/Debugging/DebugManager.cs.meta b/Assets/FishNet/Runtime/Managing/Debugging/DebugManager.cs.meta new file mode 100644 index 0000000..b36061b --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Debugging/DebugManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6d0962ead4b02a34aae248fccce671ce +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Debugging/ParseLogger.cs b/Assets/FishNet/Runtime/Managing/Debugging/ParseLogger.cs new file mode 100644 index 0000000..558cfdb --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Debugging/ParseLogger.cs @@ -0,0 +1,73 @@ +#if UNITY_EDITOR || DEVELOPMENT_BUILD +using FishNet.Managing.Logging; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Transporting; +using System; +using System.Collections.Generic; +using System.Text; +using UnityEngine; + +namespace FishNet.Managing.Debugging +{ + + internal class ParseLogger + { + /// + /// Contains the last several non-split packets to arrive. This is used for debugging. + /// + private Queue _incomingPacketIds = new Queue(); + /// + /// Maximum number of packets allowed to be queued. + /// + private const int PACKET_COUNT = 5; + + /// + /// Resets data. + /// + internal void Reset() + { + _incomingPacketIds.Clear(); + } + + /// + /// Adds a packet to data. + /// + /// + internal void AddPacket(PacketId pId) + { + _incomingPacketIds.Enqueue(pId); + if (_incomingPacketIds.Count > PACKET_COUNT) + _incomingPacketIds.Dequeue(); + } + + /// + /// Prints current data. + /// + internal void Print(NetworkManager nm) + { + if (nm == null) + nm = InstanceFinder.NetworkManager; + + //Only log if a NM was found. + if (nm != null) + { + StringBuilder sb = new StringBuilder(); + foreach (PacketId item in _incomingPacketIds) + sb.Insert(0, $"{item.ToString()}{Environment.NewLine}"); + + NetworkObject lastNob = Reader.LastNetworkObject; + string nobData = (lastNob == null) ? "Unset" : $"Id {lastNob.ObjectId} on gameObject {lastNob.name}"; + NetworkBehaviour lastNb = Reader.LastNetworkBehaviour; + string nbData = (lastNb == null) ? "Unset" : lastNb.GetType().Name; + + nm.LogError($"The last {_incomingPacketIds.Count} packets to arrive are: {Environment.NewLine}{sb.ToString()}"); + nm.LogError($"The last parsed NetworkObject is {nobData}, and NetworkBehaviour {nbData}."); + } + + Reset(); + } + } + +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Debugging/ParseLogger.cs.meta b/Assets/FishNet/Runtime/Managing/Debugging/ParseLogger.cs.meta new file mode 100644 index 0000000..0b7d4f2 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Debugging/ParseLogger.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: afc241e869d97a44f8339510586dce73 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Editor.meta b/Assets/FishNet/Runtime/Managing/Editor.meta new file mode 100644 index 0000000..7f31d5b --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 30f99c38769dfc548b3b57c6866f6a44 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Editor/NetworkManagerEditor.cs b/Assets/FishNet/Runtime/Managing/Editor/NetworkManagerEditor.cs new file mode 100644 index 0000000..0f0bbfd --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Editor/NetworkManagerEditor.cs @@ -0,0 +1,71 @@ +#if UNITY_EDITOR +using FishNet.Managing.Object; +using UnityEditor; +using UnityEngine; + +namespace FishNet.Managing.Editing +{ + [CustomEditor(typeof(NetworkManager))] + public class NetworkManagerEditor : Editor + { + private SerializedProperty _logging; + private SerializedProperty _refreshDefaultPrefabs; + private SerializedProperty _runInBackground; + private SerializedProperty _dontDestroyOnLoad; + private SerializedProperty _persistence; + private SerializedProperty _spawnablePrefabs; + private SerializedProperty _objectPool; + + private void OnEnable() + { + _logging = serializedObject.FindProperty("_logging"); + _refreshDefaultPrefabs = serializedObject.FindProperty("_refreshDefaultPrefabs"); + _runInBackground = serializedObject.FindProperty("_runInBackground"); + _dontDestroyOnLoad = serializedObject.FindProperty("_dontDestroyOnLoad"); + _persistence = serializedObject.FindProperty("_persistence"); + _spawnablePrefabs = serializedObject.FindProperty("_spawnablePrefabs"); + _objectPool = serializedObject.FindProperty("_objectPool"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + NetworkManager networkManager = (NetworkManager)target; + + GUI.enabled = false; + EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour(networkManager), typeof(NetworkManager), false); + GUI.enabled = true; + + //EditorGUILayout.BeginVertical(GUI.skin.box); + //EditorGUILayout.EndVertical(); + + + EditorGUILayout.LabelField("Settings", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_runInBackground); + EditorGUILayout.PropertyField(_dontDestroyOnLoad); + EditorGUILayout.PropertyField(_persistence); + EditorGUILayout.Space(); + EditorGUI.indentLevel--; + + EditorGUILayout.LabelField("Logging", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_logging); + EditorGUILayout.Space(); + EditorGUI.indentLevel--; + + EditorGUILayout.LabelField("Prefabs", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_spawnablePrefabs); + EditorGUILayout.PropertyField(_objectPool); + EditorGUILayout.PropertyField(_refreshDefaultPrefabs); + + EditorGUI.indentLevel--; + EditorGUILayout.Space(); + + serializedObject.ApplyModifiedProperties(); + } + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Editor/NetworkManagerEditor.cs.meta b/Assets/FishNet/Runtime/Managing/Editor/NetworkManagerEditor.cs.meta new file mode 100644 index 0000000..587918d --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Editor/NetworkManagerEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4e8e16b3e97106a4980b954c56d7bbc5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Logging.meta b/Assets/FishNet/Runtime/Managing/Logging.meta new file mode 100644 index 0000000..8796da6 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Logging.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 756fcf2bc72a3214baf43099a0be9799 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Logging/LevelLoggingConfiguration.cs b/Assets/FishNet/Runtime/Managing/Logging/LevelLoggingConfiguration.cs new file mode 100644 index 0000000..dc83b59 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Logging/LevelLoggingConfiguration.cs @@ -0,0 +1,142 @@ +using FishNet.Documenting; +using System; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Managing.Logging +{ + + /// + /// Configuration ScriptableObject specifying which data to log. Used in conjuction with NetworkManager. + /// + [CreateAssetMenu(fileName = "New LevelLoggingConfiguration", menuName = "FishNet/Logging/Level Logging Configuration")] + public class LevelLoggingConfiguration : LoggingConfiguration + { + + #region Serialized. + /// + /// Type of logging to use for development builds and editor. + /// + [Tooltip("Type of logging to use for development builds and editor.")] + [SerializeField] + private LoggingType _developmentLogging = LoggingType.Common; + /// + /// Type of logging to use for GUI builds. + /// + [Tooltip("Type of logging to use for GUI builds.")] + [SerializeField] + private LoggingType _guiLogging = LoggingType.Warning; + /// + /// Type of logging to use for headless builds. + /// + [Tooltip("Type of logging to use for headless builds.")] + [SerializeField] + private LoggingType _headlessLogging = LoggingType.Error; + #endregion + + #region Private. + /// + /// True when initialized. + /// + private bool _initialized; + /// + /// Highest type which can be logged. + /// + private LoggingType _highestLoggingType = LoggingType.Off; + #endregion + + [APIExclude] + public void LoggingConstructor(bool loggingEnabled, LoggingType development, LoggingType gui, LoggingType headless) + { + base.LoggingEnabled = loggingEnabled; + _developmentLogging = development; + _guiLogging = gui; + _headlessLogging = headless; + } + + /// + /// Initializes script for use. + /// + /// + public override void InitializeOnce() + { + byte currentHighest = (byte)LoggingType.Off; +#if UNITY_SERVER //if headless. + currentHighest = Math.Max(currentHighest, (byte)_headlessLogging); +#endif +#if UNITY_EDITOR || DEVELOPMENT_BUILD //if editor or development. + currentHighest = Math.Max(currentHighest, (byte)_developmentLogging); +#endif +#if !UNITY_EDITOR && !UNITY_SERVER //if a build. + currentHighest = Math.Max(currentHighest, (byte)_guiLogging); +#endif + _highestLoggingType = (LoggingType)currentHighest; + _initialized = true; + } + + /// + /// True if can log for loggingType. + /// + /// Type of logging being filtered. + /// + public override bool CanLog(LoggingType loggingType) + { + if (!base.LoggingEnabled) + return false; + + if (!_initialized) + { +#if UNITY_EDITOR || DEVELOPMENT_BUILD + if (Application.isPlaying) + Debug.LogError("CanLog called before being initialized."); + else + return true; +#endif + return false; + } + + return ((byte)loggingType <= (byte)_highestLoggingType); + } + + /// + /// Logs a common value if can log. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void Log(string value) + { + if (CanLog(LoggingType.Common)) + Debug.Log(value); + } + + /// + /// Logs a warning value if can log. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void LogWarning(string value) + { + if (CanLog(LoggingType.Warning)) + Debug.LogWarning(value); + } + + /// + /// Logs an error value if can log. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void LogError(string value) + { + if (CanLog(LoggingType.Error)) + Debug.LogError(value); + } + + /// + /// Clones this logging configuration. + /// + /// + public override LoggingConfiguration Clone() + { + LevelLoggingConfiguration copy = ScriptableObject.CreateInstance(); + copy.LoggingConstructor(base.LoggingEnabled, _developmentLogging, _guiLogging, _headlessLogging); + return copy; + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Logging/LevelLoggingConfiguration.cs.meta b/Assets/FishNet/Runtime/Managing/Logging/LevelLoggingConfiguration.cs.meta new file mode 100644 index 0000000..109c326 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Logging/LevelLoggingConfiguration.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 925fc096350b81f4f82f4fe4ac0c4dda +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Logging/LoggingConfiguration.cs b/Assets/FishNet/Runtime/Managing/Logging/LoggingConfiguration.cs new file mode 100644 index 0000000..e3f5379 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Logging/LoggingConfiguration.cs @@ -0,0 +1,57 @@ +using FishNet.Documenting; +using System; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Managing.Logging +{ + + /// + /// Base for logging configurations. + /// + public abstract class LoggingConfiguration : ScriptableObject + { + + #region Serialized. + /// + /// True to use logging features. False to disable all logging. + /// + [Tooltip("True to use logging features. False to disable all logging.")] + public bool LoggingEnabled = true; + #endregion + + /// + /// Initializes script for use. + /// + /// + public virtual void InitializeOnce() { } + + /// + /// True if can log for loggingType. + /// + /// Type of logging being filtered. + /// + public abstract bool CanLog(LoggingType loggingType); + + /// + /// Logs a common value if can log. + /// + public abstract void Log(string value); + + /// + /// Logs a warning value if can log. + /// + public abstract void LogWarning(string value); + + /// + /// Logs an error value if can log. + /// + public abstract void LogError(string value); + + /// + /// Clones this logging configuration. + /// + /// + public abstract LoggingConfiguration Clone(); + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Logging/LoggingConfiguration.cs.meta b/Assets/FishNet/Runtime/Managing/Logging/LoggingConfiguration.cs.meta new file mode 100644 index 0000000..a448994 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Logging/LoggingConfiguration.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 438d7e99b7655114891d4fa6e9f68c7d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Logging/LoggingType.cs b/Assets/FishNet/Runtime/Managing/Logging/LoggingType.cs new file mode 100644 index 0000000..e5a82e1 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Logging/LoggingType.cs @@ -0,0 +1,25 @@ +namespace FishNet.Managing.Logging +{ + /// + /// Type of logging being filtered. + /// + public enum LoggingType : byte + { + /// + /// Disable logging. + /// + Off = 0, + /// + /// Only log errors. + /// + Error = 1, + /// + /// Log warnings and errors. + /// + Warning = 2, + /// + /// Log all common activities, warnings, and errors. + /// + Common = 3 + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Logging/LoggingType.cs.meta b/Assets/FishNet/Runtime/Managing/Logging/LoggingType.cs.meta new file mode 100644 index 0000000..96f2758 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Logging/LoggingType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8bf0a7ab3f60fe44984fcfd16d8fe7b4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/NetworkManager.Logging.cs b/Assets/FishNet/Runtime/Managing/NetworkManager.Logging.cs new file mode 100644 index 0000000..e301c9e --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/NetworkManager.Logging.cs @@ -0,0 +1,121 @@ +using FishNet.Documenting; +using FishNet.Managing.Logging; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Managing +{ + public partial class NetworkManager : MonoBehaviour + { + #region Serialized. + /// + /// Logging configuration to use. When empty default logging settings will be used. + /// + [Tooltip("Logging configuration to use. When empty default logging settings will be used.")] + [SerializeField] + private LoggingConfiguration _logging; + #endregion + + #region Const. + private const string ERROR_LOGGING_PREFIX = "Error - "; + private const string WARNING_LOGGING_PREFIX = "Warning - "; + private const string COMMON_LOGGING_PREFIX = "Log - "; + #endregion + + /// + /// Initializes logging settings. + /// + private void InitializeLogging() + { + if (_logging == null) + _logging = ScriptableObject.CreateInstance(); + else + _logging = _logging.Clone(); + + _logging.InitializeOnce(); + } + + + /// + /// True if can log for loggingType. + /// + /// + /// + [APIExclude] + public static bool StaticCanLog(LoggingType loggingType) + { + NetworkManager nm = InstanceFinder.NetworkManager; + return (nm == null) ? false : nm.CanLog(loggingType); + } + + /// + /// True if can log for loggingType. + /// + /// Type of logging being filtered. + /// + public bool CanLog(LoggingType loggingType) + { + return _logging.CanLog(loggingType); + } + + + /// + /// Performs a common log, should logging settings permit it. + /// + [APIExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StaticLog(string value) => InstanceFinder.NetworkManager?.Log(value); + + /// + /// Performs a common log, should logging settings permit it. + /// + public void Log(string value) + { + _logging.Log(value); + } + + /// + /// Performs a log using the loggingType, should logging settings permit it. + /// + public void Log(LoggingType loggingType, string value) + { + if (loggingType == LoggingType.Common) + _logging.Log(value); + else if (loggingType == LoggingType.Warning) + _logging.LogWarning(value); + else if (loggingType == LoggingType.Error) + _logging.LogError(value); + } + + /// + /// Performs a warning log, should logging settings permit it. + /// + /// + [APIExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StaticLogWarning(string value) => InstanceFinder.NetworkManager?.LogWarning(value); + /// + /// Performs a warning log, should logging settings permit it. + /// + public void LogWarning(string value) + { + _logging.LogWarning(value); + } + + /// + /// Performs an error log, should logging settings permit it. + /// + [APIExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StaticLogError(string value) => InstanceFinder.NetworkManager?.LogError(value); + /// + /// Performs an error log, should logging settings permit it. + /// + public void LogError(string value) + { + _logging.LogError(value); + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/NetworkManager.Logging.cs.meta b/Assets/FishNet/Runtime/Managing/NetworkManager.Logging.cs.meta new file mode 100644 index 0000000..aef818b --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/NetworkManager.Logging.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4200d74f31ee8144fb606ce88ad1b747 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/NetworkManager.Pro.cs b/Assets/FishNet/Runtime/Managing/NetworkManager.Pro.cs new file mode 100644 index 0000000..fd9ed2a --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/NetworkManager.Pro.cs @@ -0,0 +1,20 @@ +using FishNet.Component.ColliderRollback; +using UnityEngine; + +namespace FishNet.Managing +{ + public sealed partial class NetworkManager : MonoBehaviour + { + + #region Public. + /// + /// RollbackManager for this NetworkManager. + /// + public RollbackManager RollbackManager { get; private set; } + #endregion + + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/NetworkManager.Pro.cs.meta b/Assets/FishNet/Runtime/Managing/NetworkManager.Pro.cs.meta new file mode 100644 index 0000000..e25b92c --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/NetworkManager.Pro.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cb7fdb186794b674788273f3b211ec5b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/NetworkManager.QOL.cs b/Assets/FishNet/Runtime/Managing/NetworkManager.QOL.cs new file mode 100644 index 0000000..c04f7e1 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/NetworkManager.QOL.cs @@ -0,0 +1,259 @@ +using FishNet.Managing.Object; +using FishNet.Object; +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityComponent = UnityEngine.Component; + + +namespace FishNet.Managing +{ + public partial class NetworkManager : MonoBehaviour + { + #region Serialized. + /// + /// + /// + [Tooltip("Collection to use for spawnable objects.")] + [SerializeField] + private PrefabObjects _spawnablePrefabs; + /// + /// Collection to use for spawnable objects. + /// + public PrefabObjects SpawnablePrefabs { get => _spawnablePrefabs; set => _spawnablePrefabs = value; } + /// + /// + /// + private Dictionary _runtimeSpawnablePrefabs = new Dictionary(); + /// + /// Collection to use for spawnable objects added at runtime, such as addressables. + /// + public IReadOnlyDictionary RuntimeSpawnablePrefabs => _runtimeSpawnablePrefabs; + #endregion + + #region Private. + /// + /// Delegates waiting to be invoked when a component is registered. + /// + private Dictionary>> _pendingInvokes = new Dictionary>>(); + /// + /// Currently registered components. + /// + private Dictionary _registeredComponents = new Dictionary(); + #endregion + + /// + /// Gets the PrefabObjects to use for spawnableCollectionId. + /// + /// Type of PrefabObjects to return. This is also used to create an instance of type when createIfMissing is true. + /// Id to use. 0 will return the configured SpawnablePrefabs. + /// True to create and assign a PrefabObjects if missing for the collectionId. + /// + public PrefabObjects GetPrefabObjects(ushort spawnableCollectionId, bool createIfMissing) where T : PrefabObjects + { + if (spawnableCollectionId == 0) + { + if (createIfMissing) + { + LogError($"SpawnableCollectionId cannot be 0 when create missing is true."); + return null; + } + else + { + return SpawnablePrefabs; + } + } + + PrefabObjects po; + if (!_runtimeSpawnablePrefabs.TryGetValue(spawnableCollectionId, out po)) + { + //Do not create missing, return null for not found. + if (!createIfMissing) + return null; + + po = ScriptableObject.CreateInstance(); + po.SetCollectionId(spawnableCollectionId); + _runtimeSpawnablePrefabs[spawnableCollectionId] = po; + } + + return po; + } + + /// + /// Removes the PrefabObjects collection from memory. + /// This should only be called after you properly disposed of it's contents properly. + /// + /// CollectionId to remove. + /// True if collection was found and removed. + public bool RemoveSpawnableCollection(ushort spawnableCollectionId) + { + return _runtimeSpawnablePrefabs.Remove(spawnableCollectionId); + } + + /// + /// Gets the index a prefab uses. Can be used in conjuction with GetPrefab. + /// + /// + /// True if to get from the server collection. + /// Returns index if found, and -1 if not found. + public int GetPrefabIndex(GameObject prefab, bool asServer) + { + int count = SpawnablePrefabs.GetObjectCount(); + for (int i = 0; i < count; i++) + { + GameObject go = SpawnablePrefabs.GetObject(asServer, i).gameObject; + if (go == prefab) + return i; + } + + //Fall through, not found. + return -1; + } + + /// + /// Returns a prefab with prefabId. + /// This method will bypass object pooling. + /// + /// PrefabId to get. + /// True if getting the prefab asServer. + public NetworkObject GetPrefab(int prefabId, bool asServer) + { + return SpawnablePrefabs.GetObject(asServer, prefabId); + } + + + #region Registered components + /// + /// Invokes an action when a specified component becomes registered. Action will invoke immediately if already registered. + /// + /// Component type. + /// Action to invoke. + public void RegisterInvokeOnInstance(Action handler) where T : UnityComponent + { + T result = GetInstance(false); + //If not found yet make a pending invoke. + if (result == default(T)) + { + string tName = GetInstanceName(); + List> handlers; + if (!_pendingInvokes.TryGetValue(tName, out handlers)) + { + handlers = new List>(); + _pendingInvokes[tName] = handlers; + } + + handlers.Add(handler); + } + //Already exist, invoke right away. + else + { + handler.Invoke(result); + } + } + /// + /// Removes an action to be invokes when a specified component becomes registered. + /// + /// Component type. + /// Action to invoke. + public void UnregisterInvokeOnInstance(Action handler) where T : UnityComponent + { + string tName = GetInstanceName(); + List> handlers; + if (!_pendingInvokes.TryGetValue(tName, out handlers)) + return; + + handlers.Remove(handler); + //Do not remove pending to prevent garbage collection later on list recreation. + } + /// + /// Returns if an instance exists for type. + /// + /// + /// + public bool HasInstance() where T : UnityComponent + { + return (GetInstance(false) != null); + } + /// + /// Returns class of type if found within CodegenBase classes. + /// + /// + /// True to warn if component is not registered. + /// + public T GetInstance(bool warn = true) where T : UnityComponent + { + string tName = GetInstanceName(); + if (_registeredComponents.TryGetValue(tName, out UnityComponent result)) + return (T)result; + else if (warn) + LogWarning($"Component {tName} is not registered."); + + return default(T); + } + /// + /// Registers a new component to this NetworkManager. + /// + /// Type to register. + /// Reference of the component being registered. + /// True to replace existing references. + public void RegisterInstance(T component, bool replace = true) where T : UnityComponent + { + string tName = GetInstanceName(); + if (_registeredComponents.ContainsKey(tName) && !replace) + { + LogWarning($"Component {tName} is already registered."); + } + else + { + _registeredComponents[tName] = component; + RemoveNullPendingDelegates(); + //If in pending invokes also send these out. + if (_pendingInvokes.TryGetValue(tName, out List> dels)) + { + for (int i = 0; i < dels.Count; i++) + dels[i].Invoke(component); + /* Clear delegates but do not remove dictionary entry + * to prevent list from being re-initialized. */ + dels.Clear(); + } + } + } + /// + /// Unregisters a component from this NetworkManager. + /// + /// Type to unregister. + public void UnregisterInstance() where T : UnityComponent + { + string tName = GetInstanceName(); + _registeredComponents.Remove(tName); + } + /// + /// Removes delegates from pending invokes when may have gone missing. + /// + private void RemoveNullPendingDelegates() + { + foreach (List> handlers in _pendingInvokes.Values) + { + for (int i = 0; i < handlers.Count; i++) + { + if (handlers[i] == null) + { + handlers.RemoveAt(i); + i--; + } + } + } + } + /// + /// Returns the name to use for T. + /// + private string GetInstanceName() + { + return typeof(T).FullName; + } + #endregion + + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/NetworkManager.QOL.cs.meta b/Assets/FishNet/Runtime/Managing/NetworkManager.QOL.cs.meta new file mode 100644 index 0000000..eddffd8 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/NetworkManager.QOL.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5761633dda73e7447a3a41b87354d06e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/NetworkManager.cs b/Assets/FishNet/Runtime/Managing/NetworkManager.cs new file mode 100644 index 0000000..de29674 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/NetworkManager.cs @@ -0,0 +1,573 @@ +using FishNet.Connection; +using FishNet.Managing.Client; +using FishNet.Managing.Server; +using FishNet.Managing.Timing; +using FishNet.Managing.Transporting; +using UnityEngine; +using FishNet.Managing.Scened; +using FishNet.Authenticating; +using FishNet.Object; +using FishNet.Documenting; +using FishNet.Managing.Logging; +using System.Collections.Generic; +using System; +using FishNet.Managing.Observing; +using System.Linq; +using FishNet.Managing.Debugging; +using FishNet.Managing.Object; +using FishNet.Transporting; +using FishNet.Utility.Extension; +using FishNet.Managing.Statistic; +using FishNet.Utility.Performance; +using FishNet.Component.ColliderRollback; +using FishNet.Managing.Predicting; +using System.Runtime.CompilerServices; +#if UNITY_EDITOR +using FishNet.Editing.PrefabCollectionGenerator; +#endif + +namespace FishNet.Managing +{ + /// + /// Acts as a container for all things related to your networking session. + /// + [DefaultExecutionOrder(short.MinValue)] + [DisallowMultipleComponent] + [AddComponentMenu("FishNet/Manager/NetworkManager")] + public sealed partial class NetworkManager : MonoBehaviour + { + #region Types. + /// + /// Which socket to iterate data on first when as host. + /// + public enum HostIterationOrder + { + ServerFirst, + ClientFirst + } + /// + /// How to persist with multiple NetworkManagers. + /// + public enum PersistenceType + { + /// + /// Destroy any new NetworkManagers. + /// + DestroyNewest, + /// + /// Destroy previous NetworkManager when a new NetworkManager occurs. + /// + DestroyOldest, + /// + /// Allow multiple NetworkManagers, do not destroy any automatically. + /// + AllowMultiple + } + + #endregion + + #region Public. + /// + /// True if this instance of the NetworkManager is initialized. + /// + public bool Initialized { get; private set; } + /// + /// + /// + private static List _instances = new List(); + /// + /// Currently initialized NetworkManagers. + /// + public static IReadOnlyCollection Instances + { + get + { + /* Remove null instances of NetworkManager. + * This shouldn't happen because instances are removed + * OnDestroy but none the less something is causing + * it. */ + for (int i = 0; i < _instances.Count; i++) + { + if (_instances[i] == null) + { + _instances.RemoveAt(i); + i--; + } + } + return _instances; + } + } + + /// + /// True if server is active. + /// + public bool IsServer => ServerManager.Started; + /// + /// True if only the server is active. + /// + public bool IsServerOnly => (IsServer && !IsClient); + /// + /// True if the client is active and authenticated. + /// + public bool IsClient => (ClientManager.Started && ClientManager.Connection.Authenticated); + /// + /// True if only the client is active and authenticated. + /// + public bool IsClientOnly => (!IsServer && IsClient); + /// + /// True if client and server are active. + /// + public bool IsHost => (IsServer && IsClient); + /// + /// True if client nor server are active. + /// + public bool IsOffline => (!IsServer && !IsClient); + /// + /// PredictionManager for this NetworkManager. + /// + internal PredictionManager PredictionManager { get; private set; } + /// + /// ServerManager for this NetworkManager. + /// + public ServerManager ServerManager { get; private set; } + /// + /// ClientManager for this NetworkManager. + /// + public ClientManager ClientManager { get; private set; } + /// + /// TransportManager for this NetworkManager. + /// + public TransportManager TransportManager { get; private set; } + /// + /// TimeManager for this NetworkManager. + /// + public TimeManager TimeManager { get; private set; } + /// + /// SceneManager for this NetworkManager. + /// + public SceneManager SceneManager { get; private set; } + /// + /// ObserverManager for this NetworkManager. + /// + public ObserverManager ObserverManager { get; private set; } + /// + /// Authenticator for this NetworkManager. May be null if no Authenticator is used. + /// + [Obsolete("Use ServerManager.GetAuthenticator or ServerManager.SetAuthenticator instead.")] //Remove on 2023/06/01 + public Authenticator Authenticator => ServerManager.Authenticator; + /// + /// DebugManager for this NetworkManager. + /// + public DebugManager DebugManager { get; private set; } + /// + /// StatisticsManager for this NetworkManager. + /// + public StatisticsManager StatisticsManager { get; private set; } + /// + /// An empty connection reference. Used when a connection cannot be found to prevent object creation. + /// + [APIExclude] + public static NetworkConnection EmptyConnection { get; private set; } = new NetworkConnection(); + #endregion + + #region Internal. + /// + /// Starting index for RpcLinks. + /// + internal static ushort StartingRpcLinkIndex; + #endregion + + #region Serialized. + /// + /// True to refresh the DefaultPrefabObjects collection whenever the editor enters play mode. This is an attempt to alleviate the DefaultPrefabObjects scriptable object not refreshing when using multiple editor applications such as ParrelSync. + /// + [Tooltip("True to refresh the DefaultPrefabObjects collection whenever the editor enters play mode. This is an attempt to alleviate the DefaultPrefabObjects scriptable object not refreshing when using multiple editor applications such as ParrelSync.")] + [SerializeField] + private bool _refreshDefaultPrefabs = false; + /// + /// True to have your application run while in the background. + /// + [Tooltip("True to have your application run while in the background.")] + [SerializeField] + private bool _runInBackground = true; + /// + /// True to make this instance DontDestroyOnLoad. This is typical if you only want one NetworkManager. + /// + [Tooltip("True to make this instance DontDestroyOnLoad. This is typical if you only want one NetworkManager.")] + [SerializeField] + private bool _dontDestroyOnLoad = true; + /// + /// Object pool to use for this NetworkManager. Value may be null. + /// + [Tooltip("Object pool to use for this NetworkManager. Value may be null.")] + [SerializeField] + private ObjectPool _objectPool; + /// + /// How to persist when other NetworkManagers are introduced. + /// + [Tooltip("How to persist when other NetworkManagers are introduced.")] + [SerializeField] + private PersistenceType _persistence = PersistenceType.DestroyNewest; + #endregion + + #region Private. + /// + /// True if this NetworkManager can persist after Awake checks. + /// + private bool _canPersist; + #endregion + + #region Const. + /// + /// Maximum framerate allowed. + /// + internal const ushort MAXIMUM_FRAMERATE = 500; + #endregion + + + private void Awake() + { + InitializeLogging(); + if (!ValidateSpawnablePrefabs(true)) + return; + + if (StartingRpcLinkIndex == 0) + StartingRpcLinkIndex = (ushort)(EnumFN.GetHighestValue() + 1); + + bool isDefaultPrefabs = (SpawnablePrefabs != null && SpawnablePrefabs is DefaultPrefabObjects); +#if UNITY_EDITOR + /* If first instance then force + * default prefabs to repopulate. + * This is only done in editor because + * cloning tools sometimes don't synchronize + * scriptable object changes, which is what + * the default prefabs is. */ + if (_refreshDefaultPrefabs && _instances.Count == 0 && isDefaultPrefabs) + { + Generator.IgnorePostProcess = true; + Debug.Log("DefaultPrefabCollection is being refreshed."); + Generator.GenerateFull(); + Generator.IgnorePostProcess = false; + } +#endif + //If default prefabs then also make a new instance and sort them. + if (isDefaultPrefabs) + { + DefaultPrefabObjects originalDpo = (DefaultPrefabObjects)SpawnablePrefabs; + //If not editor then a new instance must be made and sorted. + DefaultPrefabObjects instancedDpo = ScriptableObject.CreateInstance(); + instancedDpo.AddObjects(originalDpo.Prefabs.ToList(), false); + instancedDpo.Sort(); + SpawnablePrefabs = instancedDpo; + } + + _canPersist = CanInitialize(); + if (!_canPersist) + return; + + if (TryGetComponent(out _)) + LogError($"NetworkObject component found on the NetworkManager object {gameObject.name}. This is not allowed and will cause problems. Remove the NetworkObject component from this object."); + + SpawnablePrefabs.InitializePrefabRange(0); + SpawnablePrefabs.SetCollectionId(0); + + SetDontDestroyOnLoad(); + SetRunInBackground(); + DebugManager = GetOrCreateComponent(); + TransportManager = GetOrCreateComponent(); + + ServerManager = GetOrCreateComponent(); + ClientManager = GetOrCreateComponent(); + TimeManager = GetOrCreateComponent(); + SceneManager = GetOrCreateComponent(); + ObserverManager = GetOrCreateComponent(); + RollbackManager = GetOrCreateComponent(); + PredictionManager = GetOrCreateComponent(); + StatisticsManager = GetOrCreateComponent(); + if (_objectPool == null) + _objectPool = GetOrCreateComponent(); + + InitializeComponents(); + + _instances.Add(this); + Initialized = true; + } + + private void Start() + { + ServerManager.StartForHeadless(); + } + + private void OnDestroy() + { + _instances.Remove(this); + } + + /// + /// Initializes components. To be called after all components are added. + /// + private void InitializeComponents() + { + TimeManager.InitializeOnce_Internal(this); + TimeManager.OnLateUpdate += TimeManager_OnLateUpdate; + SceneManager.InitializeOnce_Internal(this); + TransportManager.InitializeOnce_Internal(this); + ClientManager.InitializeOnce_Internal(this); + ServerManager.InitializeOnce_Internal(this); + ObserverManager.InitializeOnce_Internal(this); + RollbackManager.InitializeOnce_Internal(this); + PredictionManager.InitializeOnce_Internal(this); + StatisticsManager.InitializeOnce_Internal(this); + _objectPool.InitializeOnce(this); + } + + /// + /// Updates the frame rate based on server and client status. + /// + internal void UpdateFramerate() + { + bool clientStarted = ClientManager.Started; + bool serverStarted = ServerManager.Started; + + int frameRate = 0; + //If both client and server are started then use whichever framerate is higher. + if (clientStarted && serverStarted) + frameRate = Math.Max(ServerManager.FrameRate, ClientManager.FrameRate); + else if (clientStarted) + frameRate = ClientManager.FrameRate; + else if (serverStarted) + frameRate = ServerManager.FrameRate; + + /* Make sure framerate isn't set to max on server. + * If it is then default to tick rate. If framerate is + * less than tickrate then also set to tickrate. */ +#if UNITY_SERVER + ushort minimumServerFramerate = (ushort)(TimeManager.TickRate + 1); + if (frameRate == MAXIMUM_FRAMERATE) + frameRate = minimumServerFramerate; + else if (frameRate < TimeManager.TickRate) + frameRate = minimumServerFramerate; +#endif + //If there is a framerate to set. + if (frameRate > 0) + Application.targetFrameRate = frameRate; + } + + /// + /// Called when MonoBehaviours call LateUpdate. + /// + private void TimeManager_OnLateUpdate() + { + /* Some reason runinbackground becomes unset + * or the setting goes ignored some times when it's set + * in awake. Rather than try to fix or care why Unity + * does this just set it in LateUpdate(or Update). */ + SetRunInBackground(); + } + + + /// + /// Returns if this NetworkManager can initialize. + /// + /// + private bool CanInitialize() + { + /* If allow multiple then any number of + * NetworkManagers are allowed. Don't + * automatically destroy any. */ + if (_persistence == PersistenceType.AllowMultiple) + return true; + + List instances = Instances.ToList(); + //This is the first instance, it may initialize. + if (instances.Count == 0) + return true; + + //First instance of NM. + NetworkManager firstInstance = instances[0]; + + //If to destroy the newest. + if (_persistence == PersistenceType.DestroyNewest) + { + Log($"NetworkManager on object {gameObject.name} is being destroyed due to persistence type {_persistence}. A NetworkManager instance already exist on {firstInstance.name}."); + Destroy(gameObject); + //This one is being destroyed because its the newest. + return false; + } + //If to destroy the oldest. + else if (_persistence == PersistenceType.DestroyOldest) + { + Log($"NetworkManager on object {firstInstance.name} is being destroyed due to persistence type {_persistence}. A NetworkManager instance has been created on {gameObject.name}."); + Destroy(firstInstance.gameObject); + //This being the new one will persist, allow initialization. + return true; + } + //Unhandled. + else + { + Log($"Persistance type of {_persistence} is unhandled on {gameObject.name}. Initialization will not proceed."); + return false; + } + } + + /// + /// Validates SpawnablePrefabs field and returns if validated successfully. + /// + /// + private bool ValidateSpawnablePrefabs(bool print) + { + //If null and object is in a scene. + if (SpawnablePrefabs == null && !string.IsNullOrEmpty(gameObject.scene.name)) + { + //Always throw an error as this would cause failure. + if (print) + Debug.LogError($"SpawnablePrefabs is null on {gameObject.name}. Select the NetworkManager in scene {gameObject.scene.name} and choose a prefabs file. Choosing DefaultPrefabObjects will automatically populate prefabs for you."); + return false; + } + + return true; + } + + /// + /// Sets DontDestroyOnLoad if configured to. + /// + private void SetDontDestroyOnLoad() + { + if (_dontDestroyOnLoad) + DontDestroyOnLoad(this); + } + + /// + /// Sets Application.runInBackground to runInBackground. + /// + private void SetRunInBackground() + { + Application.runInBackground = _runInBackground; + } + + /// + /// Gets a component, creating and adding it if it does not exist. + /// + /// Value which may already be set. When not null this is returned instead. + private T GetOrCreateComponent(T presetValue = null) where T : UnityEngine.Component + { + //If already set then return set value. + if (presetValue != null) + return presetValue; + + if (gameObject.TryGetComponent(out T result)) + return result; + else + return gameObject.AddComponent(); + } + + /// + /// Clears a client collection after disposing of the NetworkConnections. + /// + /// + internal void ClearClientsCollection(Dictionary clients) + { + foreach (NetworkConnection conn in clients.Values) + conn.Dispose(); + + clients.Clear(); + } + + #region Object pool. + /// + /// Returns an instantiated copy of prefab. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public NetworkObject GetPooledInstantiated(NetworkObject prefab, bool asServer) + { + return GetPooledInstantiated(prefab, 0, asServer); + } + /// + /// Returns an instantiated copy of prefab. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public NetworkObject GetPooledInstantiated(NetworkObject prefab, ushort collectionId, bool asServer) + { + return GetPooledInstantiated(prefab.PrefabId, collectionId, asServer); + } + /// + /// Returns an instantiated copy of prefab. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public NetworkObject GetPooledInstantiated(GameObject prefab, bool asServer) + { + return GetPooledInstantiated(prefab, 0, asServer); + } + /// + /// Returns an instantiated copy of prefab. + /// + public NetworkObject GetPooledInstantiated(GameObject prefab, ushort collectionId, bool asServer) + { + NetworkObject nob; + if (!prefab.TryGetComponent(out nob)) + { + LogError($"NetworkObject was not found on {prefab}. An instantiated NetworkObject cannot be returned."); + return null; + } + else + { + return GetPooledInstantiated(nob.PrefabId, collectionId, asServer); + } + } + /// + /// Returns an instantiated object that has prefabId. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public NetworkObject GetPooledInstantiated(int prefabId, bool asServer) + { + return GetPooledInstantiated(prefabId, 0, asServer); + } + /// + /// Returns an instantiated object that has prefabId. + /// + public NetworkObject GetPooledInstantiated(int prefabId, ushort collectionId, bool asServer) + { + return _objectPool.RetrieveObject(prefabId, collectionId, asServer); + } + /// + /// Stores an instantiated object. + /// + /// Object which was instantiated. + /// + /// True to store for the server. + [Obsolete("Use StorePooledInstantiated(NetworkObject, bool)")] //Remove on 2023/06/01. + public void StorePooledInstantiated(NetworkObject instantiated, int prefabId, bool asServer) + { + StorePooledInstantiated(instantiated, asServer); + } + /// + /// Stores an instantied object. + /// + /// Object which was instantiated. + /// True to store for the server. + public void StorePooledInstantiated(NetworkObject instantiated, bool asServer) + { + _objectPool.StoreObject(instantiated, asServer); + } + #endregion + + #region Editor. +#if UNITY_EDITOR + private void OnValidate() + { + if (SpawnablePrefabs == null) + Reset(); + } + private void Reset() + { + ValidateSpawnablePrefabs(true); + } + +#endif + + #endregion + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/NetworkManager.cs.meta b/Assets/FishNet/Runtime/Managing/NetworkManager.cs.meta new file mode 100644 index 0000000..1f1685d --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/NetworkManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d2c95dfde7d73b54dbbdc23155d35d36 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Object.meta b/Assets/FishNet/Runtime/Managing/Object.meta new file mode 100644 index 0000000..6f25714 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ba48f298f92e0684db6b68c8d3fc2672 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Object/DespawnWriter.cs b/Assets/FishNet/Runtime/Managing/Object/DespawnWriter.cs new file mode 100644 index 0000000..2111ff4 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/DespawnWriter.cs @@ -0,0 +1 @@ +//Remove on 2023/06/01 \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Object/DespawnWriter.cs.meta b/Assets/FishNet/Runtime/Managing/Object/DespawnWriter.cs.meta new file mode 100644 index 0000000..ee1eaef --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/DespawnWriter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b444a2a7364932340a4a3dede4a434a9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Object/DualPrefab.cs b/Assets/FishNet/Runtime/Managing/Object/DualPrefab.cs new file mode 100644 index 0000000..b0e3096 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/DualPrefab.cs @@ -0,0 +1,16 @@ +using FishNet.Object; + +namespace FishNet.Managing.Object +{ + + /// + /// When using dual prefabs, defines which prefab to spawn for server, and which for clients. + /// + [System.Serializable] + public struct DualPrefab + { + public NetworkObject Server; + public NetworkObject Client; + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Object/DualPrefab.cs.meta b/Assets/FishNet/Runtime/Managing/Object/DualPrefab.cs.meta new file mode 100644 index 0000000..0011412 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/DualPrefab.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 76840b2b810d8fc45aeccef03122763c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.Spawning.cs b/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.Spawning.cs new file mode 100644 index 0000000..925fc7f --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.Spawning.cs @@ -0,0 +1,380 @@ +using FishNet.Connection; +using FishNet.Managing.Logging; +using FishNet.Managing.Server; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Transporting; +using FishNet.Utility.Extension; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Managing.Object +{ + public abstract partial class ManagedObjects + { + /// + /// Reads and outputs a transforms values. + /// + protected void ReadTransformProperties(Reader reader, out Vector3? localPosition, out Quaternion? localRotation, out Vector3? localScale) + { + //Read changed. + ChangedTransformProperties ctp = (ChangedTransformProperties)reader.ReadByte(); + //Position. + if (ChangedTransformPropertiesEnum.Contains(ctp, ChangedTransformProperties.LocalPosition)) + localPosition = reader.ReadVector3(); + else + localPosition = null; + //Rotation. + if (ChangedTransformPropertiesEnum.Contains(ctp, ChangedTransformProperties.LocalRotation)) + localRotation = reader.ReadQuaternion(NetworkManager.ServerManager.SpawnPacking.Rotation); + else + localRotation = null; + //Scale. + if (ChangedTransformPropertiesEnum.Contains(ctp, ChangedTransformProperties.LocalScale)) + localScale = reader.ReadVector3(); + else + localScale = null; + } + + /// + /// Writes a spawn to clients. + /// + internal void WriteSpawn_Server(NetworkObject nob, NetworkConnection connection, Writer everyoneWriter, Writer ownerWriter) + { + /* Using a number of writers to prevent rebuilding the + * packets excessively for values that are owner only + * vs values that are everyone. To save performance the + * owner writer is only written to if owner is valid. + * This makes the code a little uglier but will scale + * significantly better with more connections. + * + * EG: + * with this technique networkBehaviours are iterated + * twice if there is an owner; once for data to send to everyone + * and again for data only going to owner. + * + * The alternative would be to iterate the networkbehaviours + * for every connection it's going to and filling a single + * writer with values based on if owner or not. This would + * result in significantly more iterations. */ + PooledWriter headerWriter = WriterPool.GetWriter(); + headerWriter.WritePacketId(PacketId.ObjectSpawn); + headerWriter.WriteNetworkObjectForSpawn(nob); + if (NetworkManager.ServerManager.ShareIds || connection == nob.Owner) + headerWriter.WriteNetworkConnection(nob.Owner); + else + headerWriter.WriteInt16(-1); + + bool nested = (nob.IsNested && nob.ParentNetworkObject != null); + bool sceneObject = nob.IsSceneObject; + //Write type of spawn. + SpawnType st = SpawnType.Unset; + if (sceneObject) + st |= SpawnType.Scene; + else + st |= (nob.IsGlobal) ? SpawnType.InstantiatedGlobal : SpawnType.Instantiated; + //Add on nested if needed. + if (nested) + st |= SpawnType.Nested; + + headerWriter.WriteByte((byte)st); + //ComponentIndex for the nob. 0 is root but more appropriately there's a IsNested boolean as shown above. + headerWriter.WriteByte(nob.ComponentIndex); + //Properties on the transform which diff from serialized value. + WriteChangedTransformProperties(nob, sceneObject, nested, headerWriter); + + /* When nested the parent nob needs to be written. */ + if (nested) + headerWriter.WriteNetworkObjectId(nob.ParentNetworkObject); + + /* Writing a scene object. */ + if (sceneObject) + { + headerWriter.WriteUInt64(nob.SceneId, AutoPackType.Unpacked); + } + /* Writing a spawned object. */ + else + { + //Check to write parent behaviour or nob. + NetworkBehaviour parentNb; + Transform t = nob.transform.parent; + if (t != null) + { + parentNb = t.GetComponent(); + /* Check for a NetworkObject if there is no NetworkBehaviour. + * There is a small chance the parent object will only contain + * a NetworkObject. */ + if (parentNb == null) + { + //If null check if there is a nob. + NetworkObject parentNob = t.GetComponent(); + //ParentNob is null or not spawned. + if (!ParentIsSpawned(parentNob)) + { + headerWriter.WriteByte((byte)SpawnParentType.Unset); + } + else + { + headerWriter.WriteByte((byte)SpawnParentType.NetworkObject); + headerWriter.WriteNetworkObjectId(parentNob); + } + } + //NetworkBehaviour found on parent. + else + { + //ParentNb is null or not spawned. + if (!ParentIsSpawned(parentNb.NetworkObject)) + { + headerWriter.WriteByte((byte)SpawnParentType.Unset); + } + else + { + headerWriter.WriteByte((byte)SpawnParentType.NetworkBehaviour); + headerWriter.WriteNetworkBehaviour(parentNb); + } + } + + //True if pNob is not null, and is spawned. + bool ParentIsSpawned(NetworkObject pNob) + { + bool isNull = (pNob == null); + if (isNull || !pNob.IsSpawned) + { + /* Only log if pNob exist. Otherwise this would print if the user + * was parenting any object, which may not be desirable as they could be + * simply doing it for organization reasons. */ + if (!isNull) + NetworkManager.LogWarning($"Parent {t.name} is not spawned. {nob.name} will not have it's parent sent in the spawn message."); + return false; + } + + return true; + } + + } + //No parent. + else + { + headerWriter.WriteByte((byte)SpawnParentType.Unset); + } + + headerWriter.WriteNetworkObjectId(nob.PrefabId); + } + + //Write headers first. + everyoneWriter.WriteBytes(headerWriter.GetBuffer(), 0, headerWriter.Length); + if (nob.Owner.IsValid) + ownerWriter.WriteBytes(headerWriter.GetBuffer(), 0, headerWriter.Length); + + /* Used to write latest data which must be sent to + * clients, such as SyncTypes and RpcLinks. */ + PooledWriter tempWriter = WriterPool.GetWriter(); + //Send RpcLinks first. + foreach (NetworkBehaviour nb in nob.NetworkBehaviours) + nb.WriteRpcLinks(tempWriter); + //Add to everyone/owner. + everyoneWriter.WriteBytesAndSize(tempWriter.GetBuffer(), 0, tempWriter.Length); + if (nob.Owner.IsValid) + ownerWriter.WriteBytesAndSize(tempWriter.GetBuffer(), 0, tempWriter.Length); + + //Add most recent sync type values. + /* SyncTypes have to be populated for owner and everyone. + * The data may be unique for owner if synctypes are set + * to only go to owner. */ + WriteSyncTypes(everyoneWriter, tempWriter, SyncTypeWriteType.Observers); + //If owner is valid then populate owner writer as well. + if (nob.Owner.IsValid) + WriteSyncTypes(ownerWriter, tempWriter, SyncTypeWriteType.Owner); + + void WriteSyncTypes(Writer finalWriter, PooledWriter tWriter, SyncTypeWriteType writeType) + { + tWriter.Reset(); + foreach (NetworkBehaviour nb in nob.NetworkBehaviours) + nb.WriteSyncTypesForSpawn(tWriter, writeType); + finalWriter.WriteBytesAndSize(tWriter.GetBuffer(), 0, tWriter.Length); + } + + //Dispose of writers created in this method. + headerWriter.Dispose(); + tempWriter.Dispose(); + } + + /// + /// Writes changed transform proeprties to writer. + /// + protected void WriteChangedTransformProperties(NetworkObject nob, bool sceneObject, bool nested, Writer headerWriter) + { + /* Write changed transform properties. */ + ChangedTransformProperties ctp; + //If a scene object then get it from scene properties. + if (sceneObject || nested) + { + ctp = nob.GetTransformChanges(nob.SerializedTransformProperties); + } + else + { + PrefabObjects po = NetworkManager.GetPrefabObjects(nob.SpawnableCollectionId, false); + ctp = nob.GetTransformChanges(po.GetObject(true, nob.PrefabId).gameObject); + } + + headerWriter.WriteByte((byte)ctp); + //If properties have changed. + if (ctp != ChangedTransformProperties.Unset) + { + //Write any changed properties. + if (ChangedTransformPropertiesEnum.Contains(ctp, ChangedTransformProperties.LocalPosition)) + headerWriter.WriteVector3(nob.transform.localPosition); + if (ChangedTransformPropertiesEnum.Contains(ctp, ChangedTransformProperties.LocalRotation)) + headerWriter.WriteQuaternion(nob.transform.localRotation, NetworkManager.ServerManager.SpawnPacking.Rotation); + if (ChangedTransformPropertiesEnum.Contains(ctp, ChangedTransformProperties.LocalScale)) + headerWriter.WriteVector3(nob.transform.localScale); + } + + } + + /// + /// Writes a despawn. + /// + /// + protected void WriteDespawn(NetworkObject nob, DespawnType despawnType, Writer everyoneWriter) + { + everyoneWriter.WritePacketId(PacketId.ObjectDespawn); + everyoneWriter.WriteNetworkObjectForDespawn(nob, despawnType); + } + + /// + /// Gets transform properties by applying passed in values if they are not null, otherwise using transforms defaults. + /// + internal void GetTransformProperties(Vector3? readPos, Quaternion? readRot, Vector3? readScale, Transform defaultTransform, out Vector3 pos, out Quaternion rot, out Vector3 scale) + { + pos = (readPos == null) ? defaultTransform.localPosition : readPos.Value; + rot = (readRot == null) ? defaultTransform.localRotation : readRot.Value; + scale = (readScale == null) ? defaultTransform.localScale : readScale.Value; + } + + /// + /// Finds a scene NetworkObject and sets transform values. + /// + internal NetworkObject GetSceneNetworkObject(ulong sceneId) + { + NetworkObject nob; + SceneObjects.TryGetValueIL2CPP(sceneId, out nob); + //If found in scene objects. + if (nob == null) + NetworkManager.LogError($"SceneId of {sceneId} not found in SceneObjects. This may occur if your scene differs between client and server, if client does not have the scene loaded, or if networked scene objects do not have a SceneCondition. See ObserverManager in the documentation for more on conditions."); + + return nob; + } + + /// + /// Returns if a NetworkObject meets basic criteria for being predicted spawned. + /// + /// If not null reader will be cleared on error. + /// + protected bool CanPredictedSpawn(NetworkObject nob, NetworkConnection spawner, NetworkConnection owner, bool asServer, Reader reader = null) + { + //Does not allow predicted spawning. + if (!nob.AllowPredictedSpawning) + { + if (asServer) + spawner.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"Connection {spawner.ClientId} tried to spawn an object {nob.name} which does not support predicted spawning."); + else + NetworkManager.LogError($"Object {nob.name} does not support predicted spawning. Modify the NetworkObject component settings to allow predicted spawning."); + + reader?.Clear(); + return false; + } + //Parenting is not yet supported. + if (nob.transform.parent != null) + { + if (asServer) + spawner.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"Connection {spawner.ClientId} tried to spawn an object that is not root."); + else + NetworkManager.LogError($"Predicted spawning as a child is not supported."); + + reader?.Clear(); + return false; + } + //Nested nobs not yet supported. + if (nob.ChildNetworkObjects.Count > 0) + { + if (asServer) + spawner.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"Connection {spawner.ClientId} tried to spawn an object {nob.name} which has nested NetworkObjects."); + else + NetworkManager.LogError($"Predicted spawning prefabs which contain nested NetworkObjects is not yet supported but will be in a later release."); + + reader?.Clear(); + return false; + } + //Blocked by PredictedSpawn settings or user logic. + if ((asServer && !nob.PredictedSpawn.OnTrySpawnServer(spawner, owner)) + || (!asServer && !nob.PredictedSpawn.OnTrySpawnClient())) + { + reader?.Clear(); + return false; + } + + return true; + } + + /// + /// Returns if a NetworkObject meets basic criteria for being predicted despawned. + /// + /// If not null reader will be cleared on error. + /// + protected bool CanPredictedDespawn(NetworkObject nob, NetworkConnection despawner, bool asServer, Reader reader = null) + { + //Does not allow predicted spawning. + if (!nob.AllowPredictedDespawning) + { + if (asServer) + despawner.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"Connection {despawner.ClientId} tried to despawn an object {nob.name} which does not support predicted despawning."); + else + NetworkManager.LogError($"Object {nob.name} does not support predicted despawning. Modify the PredictedSpawn component settings to allow predicted despawning."); + + reader?.Clear(); + return false; + } + //Parenting is not yet supported. + if (nob.transform.parent != null) + { + if (asServer) + despawner.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"Connection {despawner.ClientId} tried to despawn an object that is not root."); + else + NetworkManager.LogError($"Predicted despawning as a child is not supported."); + + reader?.Clear(); + return false; + } + //Nested nobs not yet supported. + if (nob.ChildNetworkObjects.Count > 0) + { + if (asServer) + despawner.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"Connection {despawner.ClientId} tried to despawn an object {nob.name} which has nested NetworkObjects."); + else + NetworkManager.LogError($"Predicted despawning prefabs which contain nested NetworkObjects is not yet supported but will be in a later release."); + + reader?.Clear(); + return false; + } + //Blocked by PredictedSpawn settings or user logic. + if ( + (asServer && !nob.PredictedSpawn.OnTryDepawnServer(despawner)) + || (!asServer && !nob.PredictedSpawn.OnTryDespawnClient()) + ) + { + reader?.Clear(); + return false; + } + + return true; + } + + + + + + } +} + diff --git a/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.Spawning.cs.meta b/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.Spawning.cs.meta new file mode 100644 index 0000000..bb07e79 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.Spawning.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1fcb759226359ad48926ff17cbf0ec6d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.cs b/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.cs new file mode 100644 index 0000000..b32e15f --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.cs @@ -0,0 +1,414 @@ +using FishNet.Component.Observing; +using FishNet.Connection; +using FishNet.Managing.Logging; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Transporting; +using FishNet.Utility.Extension; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace FishNet.Managing.Object +{ + public abstract partial class ManagedObjects + { + #region Public. + /// + /// NetworkObjects which are currently active. + /// + public Dictionary Spawned = new Dictionary(); + /// + /// NetworkObjects which are currently active on the local client. + /// //TODO Move this to ClientObjects. + /// + internal List LocalClientSpawned = new List(); + #endregion + + #region Protected. + /// + /// Returns the next ObjectId to use. + /// + protected internal virtual int GetNextNetworkObjectId(bool errorCheck = true) => NetworkObject.UNSET_OBJECTID_VALUE; + /// + /// NetworkManager handling this. + /// + protected NetworkManager NetworkManager { get; private set; } + /// + /// Objects in currently loaded scenes. These objects can be active or inactive. + /// Key is the objectId while value is the object. Key is not the same as NetworkObject.ObjectId. + /// + protected Dictionary SceneObjects = new Dictionary(); + #endregion + + protected void Initialize(NetworkManager manager) + { + NetworkManager = manager; + } + + /// + /// Subscribes to SceneManager.SceneLoaded event. + /// + /// + internal void SubscribeToSceneLoaded(bool subscribe) + { + if (subscribe) + SceneManager.sceneLoaded += SceneManager_sceneLoaded; + else + SceneManager.sceneLoaded -= SceneManager_sceneLoaded; + } + + /// + /// Called when a scene is loaded. + /// + /// + /// + protected internal virtual void SceneManager_sceneLoaded(Scene s, LoadSceneMode arg1) { } + + /// + /// Called when a NetworkObject runs Deactivate. + /// + /// + internal virtual void NetworkObjectUnexpectedlyDestroyed(NetworkObject nob, bool asServer) + { + if (nob == null) + return; + + RemoveFromSpawned(nob, true, asServer); + } + + /// + /// Removes a NetworkedObject from spawned. + /// + private void RemoveFromSpawned(NetworkObject nob, bool unexpectedlyDestroyed, bool asServer) + { + Spawned.Remove(nob.ObjectId); + if (!asServer) + LocalClientSpawned.Remove(nob); + //Do the same with SceneObjects. + if (unexpectedlyDestroyed && nob.IsSceneObject) + RemoveFromSceneObjects(nob); + } + + /// + /// Despawns a NetworkObject. + /// + internal virtual void Despawn(NetworkObject nob, DespawnType despawnType, bool asServer) + { + if (nob == null) + { + NetworkManager.LogWarning($"Cannot despawn a null NetworkObject."); + return; + } + + //True if should be destroyed, false if deactivated. + bool destroy = false; + /* Only modify object state if asServer, + * or !asServer and not host. This is so clients, when acting as + * host, don't destroy objects they lost observation of. */ + + /* Nested prefabs can never be destroyed. Only check to + * destroy if not nested. By nested prefab, this means the object + * despawning is part of another prefab that is also a spawned + * network object. */ + if (!nob.IsNested) + { + //If as server. + if (asServer) + { + //Scene object. + if (!nob.IsSceneObject) + { + /* If client-host has visibility + * then disable and wait for client-host to get destroy + * message. Otherwise destroy immediately. */ + if (nob.Observers.Contains(NetworkManager.ClientManager.Connection)) + NetworkManager.ServerManager.Objects.AddToPending(nob); + else + destroy = true; + } + } + //Not as server. + else + { + bool isServer = NetworkManager.IsServer; + //Only check to destroy if not a scene object. + if (!nob.IsSceneObject) + { + /* If was removed from pending then also destroy. + * Pending objects are ones that exist on the server + * side only to await destruction from client side. + * Objects can also be destroyed if server is not + * active. */ + destroy = (!isServer || NetworkManager.ServerManager.Objects.RemoveFromPending(nob.ObjectId)); + } + } + } + + //Deinitialize to invoke callbacks. + nob.Deinitialize(asServer); + //Remove from match condition only if server. + if (asServer) + MatchCondition.RemoveFromMatchWithoutRebuild(nob, NetworkManager); + RemoveFromSpawned(nob, false, asServer); + + //If to destroy. + if (destroy) + { + if (despawnType == DespawnType.Destroy) + MonoBehaviour.Destroy(nob.gameObject); + else + NetworkManager.StorePooledInstantiated(nob, asServer); + } + /* If to potentially disable instead of destroy. + * This is such as something is despawning server side + * but a clientHost is present, or if a scene object. */ + else + { + //If as server. + if (asServer) + { + //If not clientHost then the object can be disabled. + if (!NetworkManager.IsClient) + nob.gameObject.SetActive(false); + } + //Not as server. + else + { + //If the server is not active then the object can be disabled. + if (!NetworkManager.IsServer) + { + nob.gameObject.SetActive(false); + } + //If also server then checks must be done. + else + { + /* Object is still spawned on the server side. This means + * the clientHost likely lost visibility. When this is the case + * update clientHost renderers. */ + if (NetworkManager.ServerManager.Objects.Spawned.ContainsKey(nob.ObjectId)) + nob.SetRenderersVisible(false); + /* No longer spawned on the server, can + * deactivate on the client. */ + else + nob.gameObject.SetActive(false); + } + } + + /* Also despawn child objects. + * This only must be done when not destroying + * as destroying would result in the despawn being + * forced. + * + * Only run if asServer as well. The server will send + * individual despawns for each child. */ + if (asServer) + { + foreach (NetworkObject childNob in nob.ChildNetworkObjects) + { + if (childNob != null && !childNob.IsDeinitializing) + Despawn(childNob, despawnType, asServer); + } + } + } + + } + + + /// + /// Updates NetworkBehaviours on nob. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected void UpdateNetworkBehavioursForSceneObject(NetworkObject nob, bool asServer) + { + //Would have already been done on server side. + if (!asServer && NetworkManager.IsServer) + return; + + InitializePrefab(nob, -1); + } + + /// + /// Initializes a prefab, not to be mistaken for initializing a spawned object. + /// + /// Prefab to initialize. + /// Index within spawnable prefabs. + public static void InitializePrefab(NetworkObject prefab, int index, ushort? collectionId = null) + { + if (prefab == null) + return; + /* Only set the Id if not -1. + * A value of -1 would indicate it's a scene + * object. */ + if (index != -1) + { + //Use +1 because 0 indicates unset. + prefab.PrefabId = (ushort)index; + if (collectionId != null) + prefab.SpawnableCollectionId = collectionId.Value; + } + + byte componentIndex = 0; + prefab.UpdateNetworkBehaviours(null, ref componentIndex); + } + + /// + /// Despawns Spawned NetworkObjects. Scene objects will be disabled, others will be destroyed. + /// + internal virtual void DespawnWithoutSynchronization(bool asServer) + { + foreach (NetworkObject nob in Spawned.Values) + DespawnWithoutSynchronization(nob, asServer, nob.GetDefaultDespawnType(), false); + + Spawned.Clear(); + } + + /// + /// Despawns a network object. + /// + /// + internal virtual void DespawnWithoutSynchronization(NetworkObject nob, bool asServer, DespawnType despawnType, bool removeFromSpawned) + { + //Null can occur when running as host and server already despawns such as when stopping. + if (nob == null) + return; + + nob.Deinitialize(asServer); + /* Only run if asServer, or not + * asServer and server isn't running. This + * prevents objects from affecting the server + * as host when being modified client side. */ + if (asServer || (!asServer && !NetworkManager.IsServer)) + { + if (removeFromSpawned) + RemoveFromSpawned(nob, false, asServer); + if (nob.IsSceneObject) + { + nob.gameObject.SetActive(false); + } + else + { + if (despawnType == DespawnType.Destroy) + MonoBehaviour.Destroy(nob.gameObject); + else + NetworkManager.StorePooledInstantiated(nob, asServer); + } + } + } + + /// + /// Adds a NetworkObject to Spawned. + /// + /// + internal void AddToSpawned(NetworkObject nob, bool asServer) + { + Spawned[nob.ObjectId] = nob; + if (!asServer) + { + LocalClientSpawned.Add(nob); + //If being added as client and is also server. + if (NetworkManager.IsServer) + nob.SetRenderersVisible(true); + } + } + + /// + /// Adds a NetworkObject to SceneObjects. + /// + /// + protected internal void AddToSceneObjects(NetworkObject nob) + { + SceneObjects[nob.SceneId] = nob; + } + + /// + /// Removes a NetworkObject from SceneObjects. + /// + /// + protected internal void RemoveFromSceneObjects(NetworkObject nob) + { + SceneObjects.Remove(nob.SceneId); + } + + /// + /// Removes a NetworkObject from SceneObjects. + /// + /// + protected internal void RemoveFromSceneObjects(ulong sceneId) + { + SceneObjects.Remove(sceneId); + } + + /// + /// Finds a NetworkObject within Spawned. + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected internal NetworkObject GetSpawnedNetworkObject(int objectId) + { + NetworkObject r; + if (!Spawned.TryGetValueIL2CPP(objectId, out r)) + NetworkManager.LogError($"Spawned NetworkObject not found for ObjectId {objectId}."); + + return r; + } + + /// + /// Tries to skip data length for a packet. + /// + /// + /// + /// + protected internal void SkipDataLength(ushort packetId, PooledReader reader, int dataLength, int rpcLinkObjectId = -1) + { + /* -1 means length wasn't set, which would suggest a reliable packet. + * Object should never be missing for reliable packets since spawns + * and despawns are reliable in order. */ + if (dataLength == (int)MissingObjectPacketLength.Reliable) + { + string msg; + bool isRpcLink = (packetId >= NetworkManager.StartingRpcLinkIndex); + if (isRpcLink) + { + msg = (rpcLinkObjectId == -1) ? + $"RPCLink of Id {(PacketId)packetId} could not be found. Remaining data will be purged." : + $"ObjectId {rpcLinkObjectId} for RPCLink {(PacketId)packetId} could not be found."; + } + else + { + msg = $"NetworkBehaviour could not be found for packetId {(PacketId)packetId}. Remaining data will be purged."; + } + + /* Default logging for server is errors only. Use error on client and warning + * on servers to reduce chances of allocation attacks. */ +#if DEVELOPMENT_BUILD || UNITY_EDITOR || !UNITY_SERVER + NetworkManager.LogError(msg); +#else + if (NetworkManager.CanLog(LoggingType.Warning)) + Debug.LogWarning(msg); +#endif + reader.Clear(); + } + /* If length is known then is unreliable packet. It's possible + * this packetId arrived before or after the object was spawned/destroyed. + * Skip past the data for this packet and use rest in reader. With non-linked + * RPCs length is sent before object information. */ + else if (dataLength >= 0) + { + reader.Skip(Math.Min(dataLength, reader.Remaining)); + } + /* -2 indicates the length is very long. Don't even try saving + * the packet, user shouldn't be sending this much data over unreliable. */ + else if (dataLength == (int)MissingObjectPacketLength.PurgeRemaiming) + { + reader.Clear(); + } + } + + } + +} diff --git a/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.cs.meta b/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.cs.meta new file mode 100644 index 0000000..345e662 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e1363007244792145846afddc31ac12c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Object/ObjectSpawnType.cs b/Assets/FishNet/Runtime/Managing/Object/ObjectSpawnType.cs new file mode 100644 index 0000000..04e590f --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/ObjectSpawnType.cs @@ -0,0 +1,31 @@ +using FishNet.Documenting; + +namespace FishNet.Managing.Object +{ + internal enum SpawnType : byte + { + Unset = 0, + Nested = 1, + Scene = 2, + Instantiated = 4, + InstantiatedGlobal = 8, + } + + [APIExclude] + internal static partial class SpawnTypeEnum + { + /// + /// Returns if whole contains part. + /// + /// + /// + /// + public static bool Contains(SpawnType whole, SpawnType part) + { + return (whole & part) == part; + } + } + + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Object/ObjectSpawnType.cs.meta b/Assets/FishNet/Runtime/Managing/Object/ObjectSpawnType.cs.meta new file mode 100644 index 0000000..39d74c8 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/ObjectSpawnType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ed15edf5a1a100d45b05f6adace574cd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Object/PrefabObjects.meta b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects.meta new file mode 100644 index 0000000..d227d92 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 681483aaaf105014b93c3c89c7f43fda +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/DefaultPrefabObjects.cs b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/DefaultPrefabObjects.cs new file mode 100644 index 0000000..08df7b4 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/DefaultPrefabObjects.cs @@ -0,0 +1,104 @@ +using FishNet.Documenting; +using FishNet.Object.Helping; +using System.Collections.Generic; +using UnityEngine; +#if UNITY_EDITOR +using FishNet.Editing; +using UnityEditor; +#endif +using FishNet.Object; + +namespace FishNet.Managing.Object +{ + + [APIExclude] + //[CreateAssetMenu(fileName = "New DefaultPrefabObjects", menuName = "FishNet/Spawnable Prefabs/Default Prefab Objects")] + public class DefaultPrefabObjects : SinglePrefabObjects + { + /// + /// Sets asset path hashes for prefabs starting at index, or if missing. + /// Returns true if one or more NetworkObjects were updated. + internal bool SetAssetPathHashes(int index) + { +#if UNITY_EDITOR + bool dirtied = false; + int count = base.GetObjectCount(); + + if (count == 0) + return false; + if (index < 0 || index >= count) + { + Debug.LogError($"Index {index} is out of range when trying to set asset path hashes. Collection length is {count}. Defaulf prefabs may need to be rebuilt."); + return false; + } + + for (int i = 0; i < count; i++) + { + NetworkObject n = base.Prefabs[i]; + if (i < index) + continue; + + string pathAndName = $"{AssetDatabase.GetAssetPath(n.gameObject)}{n.gameObject.name}"; + ulong hashcode = Hashing.GetStableHash64(pathAndName); + //Already set. + if (n.AssetPathHash == hashcode) + continue; + + n.SetAssetPathHash(hashcode); + EditorUtility.SetDirty(n); + dirtied = true; + } + + return dirtied; +#else + return false; +#endif + } + + /// + /// Sorts prefabs by name and path hashcode. + /// + internal void Sort() + { + if (base.GetObjectCount() == 0) + return; + + Dictionary hashcodesAndNobs = new Dictionary(); + List hashcodes = new List(); + + bool error = false; + foreach (NetworkObject n in base.Prefabs) + { + hashcodes.Add(n.AssetPathHash); + //If hashcode is 0 something is wrong + if (n.AssetPathHash == 0) + { + error = true; + Debug.LogError($"AssetPathHash is not set for GameObject {n.name}."); + + } + hashcodesAndNobs.Add(n.AssetPathHash, n); + } + //An error occured, no reason to continue. + if (error) + { + Debug.LogError($"One or more NetworkObject prefabs did not have their AssetPathHash set. This usually occurs when a prefab cannot be saved. Check the specified prefabs for missing scripts or serialization errors and correct them, then use Fish-Networking -> Refresh Default Prefabs."); + return; + } + + //Once all hashes have been made re-add them to prefabs sorted. + hashcodes.Sort(); + //Build to a new list using sorted hashcodes. + List sortedNobs = new List(); + foreach (ulong hc in hashcodes) + sortedNobs.Add(hashcodesAndNobs[hc]); + + base.Clear(); + base.AddObjects(sortedNobs, false); + } + + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/DefaultPrefabObjects.cs.meta b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/DefaultPrefabObjects.cs.meta new file mode 100644 index 0000000..1258c30 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/DefaultPrefabObjects.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3ad70174b079c2f4ebc7931d3dd1af6f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/DualPrefabObjects.cs b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/DualPrefabObjects.cs new file mode 100644 index 0000000..45aff7c --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/DualPrefabObjects.cs @@ -0,0 +1,136 @@ +using FishNet.Documenting; +using FishNet.Object; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Managing.Object +{ + + //document + [APIExclude] + [CreateAssetMenu(fileName = "New DualPrefabObjects", menuName = "FishNet/Spawnable Prefabs/Dual Prefab Objects")] + public class DualPrefabObjects : PrefabObjects + { + /// + /// + /// + [Tooltip("Prefabs which may be spawned.")] + [SerializeField] + private List _prefabs = new List(); + /// + /// Prefabs which may be spawned. + /// + public IReadOnlyCollection Prefabs => _prefabs; + + public override void Clear() + { + _prefabs.Clear(); + } + public override int GetObjectCount() + { + return _prefabs.Count; + } + + public override NetworkObject GetObject(bool asServer, int id) + { + if (id < 0 || id >= _prefabs.Count) + { + NetworkManager.StaticLogError($"PrefabId {id} is out of range."); + return null; + } + else + { + DualPrefab dp = _prefabs[id]; + NetworkObject nob = (asServer) ? dp.Server : dp.Client; + if (nob == null) + { + string lookupSide = (asServer) ? "server" : "client"; + NetworkManager.StaticLogError($"Prefab for {lookupSide} on id {id} is null "); + } + + return nob; + } + } + + public override void RemoveNull() + { + for (int i = 0; i < _prefabs.Count; i++) + { + if (_prefabs[i].Server == null || _prefabs[i].Client == null) + { + _prefabs.RemoveAt(i); + i--; + } + } + + if (Application.isPlaying) + InitializePrefabRange(0); + } + + public override void AddObject(DualPrefab dualPrefab, bool checkForDuplicates = false) + { + AddObjects(new DualPrefab[] { dualPrefab }, checkForDuplicates); + } + + public override void AddObjects(List dualPrefabs, bool checkForDuplicates = false) + { + AddObjects(dualPrefabs.ToArray(), checkForDuplicates); + } + + public override void AddObjects(DualPrefab[] dualPrefabs, bool checkForDuplicates = false) + { + if (!checkForDuplicates) + { + _prefabs.AddRange(dualPrefabs); + } + else + { + foreach (DualPrefab dp in dualPrefabs) + AddUniqueNetworkObjects(dp); + } + + if (Application.isPlaying) + InitializePrefabRange(0); + } + + private void AddUniqueNetworkObjects(DualPrefab dp) + { + for (int i = 0; i < _prefabs.Count; i++) + { + if (_prefabs[i].Server == dp.Server && _prefabs[i].Client == dp.Client) + return; + } + + _prefabs.Add(dp); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void InitializePrefabRange(int startIndex) + { + for (int i = startIndex; i < _prefabs.Count; i++) + { + ManagedObjects.InitializePrefab(_prefabs[i].Server, i, CollectionId); + ManagedObjects.InitializePrefab(_prefabs[i].Client, i, CollectionId); + } + } + + + #region Unused. + public override void AddObject(NetworkObject networkObject, bool checkForDuplicates = false) + { + NetworkManager.StaticLogError($"Single prefabs are not supported with DualPrefabObjects. Make a SinglePrefabObjects asset instead."); + } + + public override void AddObjects(List networkObjects, bool checkForDuplicates = false) + { + NetworkManager.StaticLogError($"Single prefabs are not supported with DualPrefabObjects. Make a SinglePrefabObjects asset instead."); + } + + public override void AddObjects(NetworkObject[] networkObjects, bool checkForDuplicates = false) + { + NetworkManager.StaticLogError($"Single prefabs are not supported with DualPrefabObjects. Make a SinglePrefabObjects asset instead."); + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/DualPrefabObjects.cs.meta b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/DualPrefabObjects.cs.meta new file mode 100644 index 0000000..4acd585 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/DualPrefabObjects.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e4b890523e001c74a9a2bf0d6340e5f7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/PrefabObjects.cs b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/PrefabObjects.cs new file mode 100644 index 0000000..cb4133a --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/PrefabObjects.cs @@ -0,0 +1,36 @@ +using FishNet.Documenting; +using FishNet.Object; +using System.Collections.Generic; +using UnityEngine; + +namespace FishNet.Managing.Object +{ + //document + [APIExclude] + public abstract class PrefabObjects : ScriptableObject + { + /// + /// CollectionId for this PrefabObjects. + /// + public ushort CollectionId { get; private set; } + /// + /// Sets CollectionIdValue. + /// + internal void SetCollectionId(ushort id) => CollectionId = id; + + public abstract void Clear(); + public abstract int GetObjectCount(); + public abstract NetworkObject GetObject(bool asServer, int id); + public abstract void RemoveNull(); + public abstract void AddObject(NetworkObject networkObject, bool checkForDuplicates = false); + public abstract void AddObjects(List networkObjects, bool checkForDuplicates = false); + public abstract void AddObjects(NetworkObject[] networkObjects, bool checkForDuplicates = false); + public abstract void AddObject(DualPrefab dualPrefab, bool checkForDuplicates = false); + public abstract void AddObjects(List dualPrefab, bool checkForDuplicates = false); + public abstract void AddObjects(DualPrefab[] dualPrefab, bool checkForDuplicates = false); + public abstract void InitializePrefabRange(int startIndex); + + + + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/PrefabObjects.cs.meta b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/PrefabObjects.cs.meta new file mode 100644 index 0000000..4b4ef77 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/PrefabObjects.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c5a7beb0d6ee75a4fb1f058eb3e2640a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/SinglePrefabObjects.cs b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/SinglePrefabObjects.cs new file mode 100644 index 0000000..d3e06af --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/SinglePrefabObjects.cs @@ -0,0 +1,128 @@ +using FishNet.Documenting; +using FishNet.Object; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Managing.Object +{ + //document + [APIExclude] + [CreateAssetMenu(fileName = "New SinglePrefabObjects", menuName = "FishNet/Spawnable Prefabs/Single Prefab Objects")] + public class SinglePrefabObjects : PrefabObjects + { + /// + /// + /// + [Tooltip("Prefabs which may be spawned.")] + [SerializeField] + private List _prefabs = new List(); + /// + /// Prefabs which may be spawned. + /// + public IReadOnlyList Prefabs => _prefabs; + + public override void Clear() + { + _prefabs.Clear(); + } + public override int GetObjectCount() + { + return _prefabs.Count; + } + public override NetworkObject GetObject(bool asServer, int id) + { + if (id < 0 || id >= _prefabs.Count) + { + NetworkManager.StaticLogError($"PrefabId {id} is out of range."); + return null; + } + else + { + NetworkObject nob = _prefabs[id]; + if (nob == null) + NetworkManager.StaticLogError($"Prefab on id {id} is null."); + + return nob; + } + } + + public override void RemoveNull() + { + for (int i = 0; i < _prefabs.Count; i++) + { + if (_prefabs[i] == null) + { + _prefabs.RemoveAt(i); + i--; + } + } + + if (Application.isPlaying) + InitializePrefabRange(0); + } + + public override void AddObject(NetworkObject networkObject, bool checkForDuplicates = false) + { + if (!checkForDuplicates) + _prefabs.Add(networkObject); + else + AddUniqueNetworkObject(networkObject); + + if (Application.isPlaying) + InitializePrefabRange(0); + } + + public override void AddObjects(List networkObjects, bool checkForDuplicates = false) + { + if (!checkForDuplicates) + { + _prefabs.AddRange(networkObjects); + } + else + { + foreach (NetworkObject nob in networkObjects) + AddUniqueNetworkObject(nob); + } + + if (Application.isPlaying) + InitializePrefabRange(0); + } + public override void AddObjects(NetworkObject[] networkObjects, bool checkForDuplicates = false) + { + AddObjects(networkObjects.ToList(), checkForDuplicates); + } + + private void AddUniqueNetworkObject(NetworkObject nob) + { + if (!_prefabs.Contains(nob)) + _prefabs.Add(nob); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void InitializePrefabRange(int startIndex) + { + for (int i = startIndex; i < _prefabs.Count; i++) + ManagedObjects.InitializePrefab(_prefabs[i], i, CollectionId); + } + + + #region Unused. + public override void AddObject(DualPrefab dualPrefab, bool checkForDuplicates = false) + { + NetworkManager.StaticLogError($"Dual prefabs are not supported with SinglePrefabObjects. Make a DualPrefabObjects asset instead."); + } + + public override void AddObjects(List dualPrefab, bool checkForDuplicates = false) + { + NetworkManager.StaticLogError($"Dual prefabs are not supported with SinglePrefabObjects. Make a DualPrefabObjects asset instead."); + } + + public override void AddObjects(DualPrefab[] dualPrefab, bool checkForDuplicates = false) + { + NetworkManager.StaticLogError($"Dual prefabs are not supported with SinglePrefabObjects. Make a DualPrefabObjects asset instead."); + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/SinglePrefabObjects.cs.meta b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/SinglePrefabObjects.cs.meta new file mode 100644 index 0000000..0cf790b --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/SinglePrefabObjects.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4489d77032a81ef42b0067acf2737d4d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Object/SpawnParentType.cs b/Assets/FishNet/Runtime/Managing/Object/SpawnParentType.cs new file mode 100644 index 0000000..ff3a421 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/SpawnParentType.cs @@ -0,0 +1,10 @@ +namespace FishNet.Managing.Object +{ + public enum SpawnParentType : byte + { + Unset = 0, + NetworkObject = 1, + NetworkBehaviour = 2 + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Object/SpawnParentType.cs.meta b/Assets/FishNet/Runtime/Managing/Object/SpawnParentType.cs.meta new file mode 100644 index 0000000..d346b30 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/SpawnParentType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cbace351ced9ff94eb294dbb2e1d6a75 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Object/SpawnWriter.cs b/Assets/FishNet/Runtime/Managing/Object/SpawnWriter.cs new file mode 100644 index 0000000..2111ff4 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/SpawnWriter.cs @@ -0,0 +1 @@ +//Remove on 2023/06/01 \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Object/SpawnWriter.cs.meta b/Assets/FishNet/Runtime/Managing/Object/SpawnWriter.cs.meta new file mode 100644 index 0000000..a3d8138 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/SpawnWriter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fd305e51107fc3441a6f52636c27298f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Observing.meta b/Assets/FishNet/Runtime/Managing/Observing.meta new file mode 100644 index 0000000..db0e55f --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Observing.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9c466e864bb3b114ab6e933d624f9934 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Observing/Editor.meta b/Assets/FishNet/Runtime/Managing/Observing/Editor.meta new file mode 100644 index 0000000..35d4018 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Observing/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b070cdff750cb4542bf0770a57f9936a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Observing/Editor/ObserverManagerEditor.cs b/Assets/FishNet/Runtime/Managing/Observing/Editor/ObserverManagerEditor.cs new file mode 100644 index 0000000..bd6aeb9 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Observing/Editor/ObserverManagerEditor.cs @@ -0,0 +1,53 @@ +#if UNITY_EDITOR +using UnityEditor; +using UnityEngine; + +namespace FishNet.Managing.Observing.Editing +{ + + + [CustomEditor(typeof(ObserverManager), true)] + [CanEditMultipleObjects] + public class ObserverManagerEditor : Editor + { + private SerializedProperty _useNetworkLod; + private SerializedProperty _levelOfDetailDistances; + private SerializedProperty _updateHostVisibility; + private SerializedProperty _defaultConditions; + + protected virtual void OnEnable() + { + _useNetworkLod = serializedObject.FindProperty(nameof(_useNetworkLod)); + _levelOfDetailDistances = serializedObject.FindProperty(nameof(_levelOfDetailDistances)); + _updateHostVisibility = serializedObject.FindProperty(nameof(_updateHostVisibility)); + _defaultConditions = serializedObject.FindProperty(nameof(_defaultConditions)); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + GUI.enabled = false; + EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour((ObserverManager)target), typeof(ObserverManager), false); + GUI.enabled = true; + + + EditorGUILayout.PropertyField(_useNetworkLod); + if (_useNetworkLod.boolValue) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_levelOfDetailDistances); + EditorGUI.indentLevel--; + } + + EditorGUILayout.PropertyField(_updateHostVisibility); + EditorGUILayout.PropertyField(_defaultConditions); + + EditorGUILayout.Space(); + + serializedObject.ApplyModifiedProperties(); + } + + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Observing/Editor/ObserverManagerEditor.cs.meta b/Assets/FishNet/Runtime/Managing/Observing/Editor/ObserverManagerEditor.cs.meta new file mode 100644 index 0000000..3076866 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Observing/Editor/ObserverManagerEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 86e21ffc228c07d4891b6e74080b8c90 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Observing/ObserverManager.cs b/Assets/FishNet/Runtime/Managing/Observing/ObserverManager.cs new file mode 100644 index 0000000..9970054 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Observing/ObserverManager.cs @@ -0,0 +1,324 @@ +using FishNet.Connection; //remove on 2023/01/01 move to correct folder. +using FishNet.Object; +using FishNet.Observing; +using FishNet.Utility.Constant; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; +using UnityEngine.Serialization; + +[assembly: InternalsVisibleTo(UtilityConstants.DEMOS_ASSEMBLY_NAME)] +namespace FishNet.Managing.Observing +{ + /// + /// Additional options for managing the observer system. + /// + [DisallowMultipleComponent] + [AddComponentMenu("FishNet/Manager/ObserverManager")] + public sealed class ObserverManager : MonoBehaviour + { + #region Internal. + /// + /// Current index to use for level of detail based on tick. + /// + internal byte LevelOfDetailIndex { get; private set; } + #endregion + + #region Serialized. + /// + /// + /// + [Tooltip("True to use the NetworkLOD system.")] + [SerializeField] + private bool _useNetworkLod; + /// + /// True to use the NetworkLOD system. + /// + /// + internal bool GetUseNetworkLod() => _useNetworkLod; + /// + /// Distance for each level of detal. + /// + internal List GetLevelOfDetailDistances() => (_useNetworkLod) ? _levelOfDetailDistances : _singleLevelOfDetailDistances; + [Tooltip("Distance for each level of detal.")] + [SerializeField] + private List _levelOfDetailDistances = new List(); + /// + /// Returned when network LOD is off. Value contained is one level of detail with max distance. + /// + private List _singleLevelOfDetailDistances = new List() { float.MaxValue }; + /// + /// + /// + [Tooltip("True to update visibility for clientHost based on if they are an observer or not.")] + [FormerlySerializedAs("_setHostVisibility")] + [SerializeField] + private bool _updateHostVisibility = true; + /// + /// True to update visibility for clientHost based on if they are an observer or not. + /// + public bool UpdateHostVisibility + { + get => _updateHostVisibility; + private set => _updateHostVisibility = value; + } + /// + /// + /// + [Tooltip("Default observer conditions for networked objects.")] + [SerializeField] + private List _defaultConditions = new List(); + #endregion + + #region Private. + /// + /// NetworkManager on object. + /// + private NetworkManager _networkManager; + /// + /// Intervals for each level of detail. + /// + private uint[] _levelOfDetailIntervals; + #endregion + + private void Awake() + { + if (_useNetworkLod && _levelOfDetailDistances.Count > 1) + { + Debug.LogWarning("Network Level of Detail has been disabled while bugs are resolved in relation to this feature. You do not need to make any changes to your project. This warning will be removed once all issues are resolved."); + _useNetworkLod = false; + } + } + + /// + /// Initializes this script for use. + /// + /// + internal void InitializeOnce_Internal(NetworkManager manager) + { + _networkManager = manager; + ValidateLevelOfDetails(); + } + + /// + /// Sets a new value for UpdateHostVisibility. + /// + /// New value. + /// Which objects to update. + public void SetUpdateHostVisibility(bool value, HostVisibilityUpdateTypes updateType) + { + //Unchanged. + if (value == UpdateHostVisibility) + return; + + /* Update even if server state is not known. + * The setting should be updated so when the server + * does start spawned objects have latest setting. */ + if (HostVisibilityUpdateContains(updateType, HostVisibilityUpdateTypes.Manager)) + UpdateHostVisibility = value; + + /* If to update spawned as well then update all networkobservers + * with the setting and also update renderers. */ + if (_networkManager.IsServer && HostVisibilityUpdateContains(updateType, HostVisibilityUpdateTypes.Spawned)) + { + NetworkConnection clientConn = _networkManager.ClientManager.Connection; + foreach (NetworkObject n in _networkManager.ServerManager.Objects.Spawned.Values) + { + n.NetworkObserver.SetUpdateHostVisibility(value); + + //Only check to update renderers if clientHost. If not client then clientConn won't be active. + if (clientConn.IsActive) + n.SetRenderersVisible(n.Observers.Contains(clientConn), true); + } + } + + bool HostVisibilityUpdateContains(HostVisibilityUpdateTypes whole, HostVisibilityUpdateTypes part) + { + return (whole & part) == part; + } + } + + /// + /// Adds default observer conditions to nob and returns the NetworkObserver used. + /// + internal NetworkObserver AddDefaultConditions(NetworkObject nob) + { + bool isGlobal = (nob.IsGlobal && !nob.IsSceneObject); + bool obsAdded; + + NetworkObserver result; + if (!nob.TryGetComponent(out result)) + { + obsAdded = true; + result = nob.gameObject.AddComponent(); + } + else + { + obsAdded = false; + } + + /* NetworkObserver is null and there are no + * conditions to add. Nothing will change by adding + * the NetworkObserver component so exit early. */ + if (!obsAdded && _defaultConditions.Count == 0) + return result; + + //If the NetworkObserver component was just added. + if (obsAdded) + { + /* Global nobs do not need a NetworkObserver. + * Ultimately, a global NetworkObject is one without + * any conditions. */ + if (isGlobal) + return result; + //If there are no conditions then there's nothing to add. + if (_defaultConditions.Count == 0) + return result; + /* If here then there not a global networkobject and there are conditions to use. + * Since the NetworkObserver is being added fresh, set OverrideType to UseManager + * so that the NetworkObserver is populated with the manager conditions. */ + result.OverrideType = NetworkObserver.ConditionOverrideType.UseManager; + } + //NetworkObject has a NetworkObserver already on it. + else + { + //If global the NetworkObserver has to be cleared and set to ignore manager. + if (isGlobal) + { + result.ObserverConditionsInternal.Clear(); + result.OverrideType = NetworkObserver.ConditionOverrideType.IgnoreManager; + } + } + + //If ignoring manager then use whatever is already configured. + if (result.OverrideType == NetworkObserver.ConditionOverrideType.IgnoreManager) + { + //Do nothing. + } + //If using manager then replace all with conditions. + else if (result.OverrideType == NetworkObserver.ConditionOverrideType.UseManager) + { + result.ObserverConditionsInternal.Clear(); + AddMissing(result); + } + //Adding only new. + else if (result.OverrideType == NetworkObserver.ConditionOverrideType.AddMissing) + { + AddMissing(result); + } + + void AddMissing(NetworkObserver networkObserver) + { + int count = _defaultConditions.Count; + for (int i = 0; i < count; i++) + { + ObserverCondition oc = _defaultConditions[i]; + if (!networkObserver.ObserverConditionsInternal.Contains(oc)) + networkObserver.ObserverConditionsInternal.Add(oc); + } + } + + return result; + } + + /// + /// Gets the tick interval to use for a lod level. + /// + /// + /// + public byte GetLevelOfDetailInterval(byte lodLevel) + { + if (LevelOfDetailIndex == 0) + return 1; + + return (byte)System.Math.Pow(2, lodLevel); + } + + /// + /// Calculates and sets the current level of detail index for the tick. + /// + internal void CalculateLevelOfDetail(uint tick) + { + int count = GetLevelOfDetailDistances().Count; + for (int i = (count - 1); i > 0; i--) + { + uint interval = _levelOfDetailIntervals[i]; + if (tick % interval == 0) + { + LevelOfDetailIndex = (byte)i; + return; + } + } + + //If here then index is 0 and interval is every tick. + LevelOfDetailIndex = 0; + } + + /// + /// Validates that level of detail intervals are proper. + /// + private void ValidateLevelOfDetails() + { + if (!_useNetworkLod) + return; + + //No distances specified. + if (_levelOfDetailDistances == null || _levelOfDetailDistances.Count == 0) + { + if (_networkManager != null) + { + _networkManager.LogWarning("Level of detail distances contains no entries. NetworkLOD has been disabled."); + _useNetworkLod = false; + } + return; + } + + //Make sure every distance is larger than the last. + float lastDistance = float.MinValue; + foreach (float dist in _levelOfDetailDistances) + { + if (dist <= 0f || dist <= lastDistance) + { + if (_networkManager != null) + { + _networkManager.LogError($"Level of detail distances must be greater than 0f, and each distance larger than the previous. NetworkLOD has been disabled."); + _useNetworkLod = false; + } + return; + } + lastDistance = dist; + } + + int maxEntries = 8; + //Too many distances. + if (_levelOfDetailDistances.Count > maxEntries) + { + _networkManager?.LogWarning("There can be a maximum of 8 level of detail distances. Entries beyond this quantity have been discarded."); + while (_levelOfDetailDistances.Count > maxEntries) + _levelOfDetailDistances.RemoveAt(_levelOfDetailDistances.Count - 1); + } + + if (Application.isPlaying) + { + //Build intervals and sqr distances. + int count = _levelOfDetailDistances.Count; + _levelOfDetailIntervals = new uint[count]; + for (int i = (count - 1); i > 0; i--) + { + uint power = (uint)Mathf.Pow(2, i); + _levelOfDetailIntervals[i] = power; + + } + //Sqr + for (int i = 0; i < count; i++) + { + float dist = _levelOfDetailDistances[i]; + dist *= dist; + _levelOfDetailDistances[i] = dist; + } + } + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Observing/ObserverManager.cs.meta b/Assets/FishNet/Runtime/Managing/Observing/ObserverManager.cs.meta new file mode 100644 index 0000000..ed04656 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Observing/ObserverManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7d331f979d46e8e4a9fc90070c596d44 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Prediction.meta b/Assets/FishNet/Runtime/Managing/Prediction.meta new file mode 100644 index 0000000..bdb3631 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Prediction.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +<<<<<<<< HEAD:Assets/FishNet/Runtime/Managing/Prediction.meta +guid: 549a94b4ad77ccc4b8511ccb476d767c +======== +guid: 678ea4b95d0063048acf8abf6928acf4 +>>>>>>>> origin/3-pre-3.1:Assets/Test/InitializeOrder.meta +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Prediction/Editor.meta b/Assets/FishNet/Runtime/Managing/Prediction/Editor.meta new file mode 100644 index 0000000..a928c88 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Prediction/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f57d8859721e294489f2991b0458aea9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Prediction/Editor/PredictionManagerEditor.cs b/Assets/FishNet/Runtime/Managing/Prediction/Editor/PredictionManagerEditor.cs new file mode 100644 index 0000000..a94ac6e --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Prediction/Editor/PredictionManagerEditor.cs @@ -0,0 +1,74 @@ +#if UNITY_EDITOR +using UnityEditor; +using UnityEngine; + +namespace FishNet.Managing.Predicting.Editing +{ + + + [CustomEditor(typeof(PredictionManager), true)] + [CanEditMultipleObjects] + public class PredictionManagerEditor : Editor + { + private SerializedProperty _dropExcessiveReplicates; + private SerializedProperty _maximumServerReplicates; + private SerializedProperty _maximumConsumeCount; + private SerializedProperty _redundancyCount; + private SerializedProperty _allowPredictedSpawning; + private SerializedProperty _reservedObjectIds; + + + protected virtual void OnEnable() + { + _dropExcessiveReplicates = serializedObject.FindProperty("_dropExcessiveReplicates"); + _maximumServerReplicates = serializedObject.FindProperty("_maximumServerReplicates"); + _maximumConsumeCount = serializedObject.FindProperty("_maximumConsumeCount"); + _redundancyCount = serializedObject.FindProperty("_redundancyCount"); + _allowPredictedSpawning = serializedObject.FindProperty(nameof(_allowPredictedSpawning)); + _reservedObjectIds = serializedObject.FindProperty(nameof(_reservedObjectIds)); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + GUI.enabled = false; + EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour((PredictionManager)target), typeof(PredictionManager), false); + GUI.enabled = true; + + EditorGUILayout.LabelField("Server", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_dropExcessiveReplicates); + EditorGUI.indentLevel++; + if (_dropExcessiveReplicates.boolValue == true) + { + EditorGUILayout.PropertyField(_maximumServerReplicates); + } + else + { + EditorGUILayout.PropertyField(_maximumConsumeCount); + } + EditorGUI.indentLevel--; + EditorGUI.indentLevel--; + + EditorGUILayout.LabelField("Client", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_redundancyCount); + EditorGUI.indentLevel--; + + EditorGUILayout.PropertyField(_allowPredictedSpawning); + if (_allowPredictedSpawning.boolValue == true) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_reservedObjectIds); + EditorGUI.indentLevel--; + } + + EditorGUILayout.Space(); + + serializedObject.ApplyModifiedProperties(); + } + + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Prediction/Editor/PredictionManagerEditor.cs.meta b/Assets/FishNet/Runtime/Managing/Prediction/Editor/PredictionManagerEditor.cs.meta new file mode 100644 index 0000000..a2276c4 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Prediction/Editor/PredictionManagerEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5382c1ab98f25c8439d23140d36651fe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Prediction/PredictionManager.cs b/Assets/FishNet/Runtime/Managing/Prediction/PredictionManager.cs new file mode 100644 index 0000000..694b3b7 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Prediction/PredictionManager.cs @@ -0,0 +1,323 @@ +using FishNet.Documenting; +using FishNet.Object; +using FishNet.Serializing.Helping; +using FishNet.Transporting; +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityScene = UnityEngine.SceneManagement.Scene; + + +namespace FishNet.Managing.Predicting +{ + + /// + /// Additional options for managing the observer system. + /// + [DisallowMultipleComponent] + [AddComponentMenu("FishNet/Manager/PredictionManager")] + public sealed class PredictionManager : MonoBehaviour + { + #region Public. + /// + /// Called before performing a reconcile on NetworkBehaviour. + /// + public event Action OnPreReconcile; + /// + /// Called after performing a reconcile on a NetworkBehaviour. + /// + public event Action OnPostReconcile; + /// + /// Called before physics is simulated when replaying a replicate method. + /// Contains the PhysicsScene and PhysicsScene2D which was simulated. + /// + public event Action OnPreReplicateReplay; + /// + /// Called after physics is simulated when replaying a replicate method. + /// Contains the PhysicsScene and PhysicsScene2D which was simulated. + /// + public event Action OnPostReplicateReplay; + /// + /// Called before the server sends a reconcile. + /// + public event Action OnPreServerReconcile; + /// + /// Called after the server sends a reconcile. + /// + public event Action OnPostServerReconcile; + /// + /// Last tick any object reconciled. + /// + public uint LastReconcileTick { get; internal set; } + /// + /// Last tick any object replicated. + /// + public uint LastReplicateTick { get; internal set; } + /// + /// True if rigidbodies are being predicted. + /// + internal bool UsingRigidbodies => (_rigidbodies.Count > 0); + /// + /// Returns if any prediction is replaying. + /// + /// + public bool IsReplaying() => _isReplaying; + private bool _isReplaying; + /// + /// Returns if scene is replaying. + /// + /// + /// + public bool IsReplaying(UnityScene scene) => _replayingScenes.Contains(scene); + #endregion + + #region Serialized. + /// + /// + /// + [Tooltip("True to drop replicates from clients which are being received excessively. This can help with attacks but may cause client to temporarily desynchronize during connectivity issues. When false the server will hold at most up to 3 seconds worth of replicates, consuming multiple per tick to clear out the buffer quicker. This is good to ensure all inputs are executed but potentially could allow speed hacking.")] + [SerializeField] + private bool _dropExcessiveReplicates = true; + /// + /// True to drop replicates from clients which are being received excessively. This can help with attacks but may cause client to temporarily desynchronize during connectivity issues. + /// When false the server will hold at most up to 3 seconds worth of replicates, consuming multiple per tick to clear out the buffer quicker. This is good to ensure all inputs are executed but potentially could allow speed hacking. + /// + internal bool DropExcessiveReplicates => _dropExcessiveReplicates; + /// + /// + /// + [Tooltip("Maximum number of replicates a server can queue per object. Higher values will put more load on the server and add replicate latency for the client.")] + [SerializeField] + private ushort _maximumServerReplicates = 15; + /// + /// Maximum number of replicates a server can queue per object. Higher values will put more load on the server and add replicate latency for the client. + /// + public ushort GetMaximumServerReplicates() => _maximumServerReplicates; + /// + /// Sets the maximum number of replicates a server can queue per object. + /// + /// + public void SetMaximumServerReplicates(ushort value) + { + _maximumServerReplicates = (ushort)Mathf.Clamp(value, MINIMUM_REPLICATE_QUEUE_SIZE, MAXIMUM_REPLICATE_QUEUE_SIZE); + } + /// + /// + /// + [Tooltip("Maximum number of excessive replicates which can be consumed per tick. Consumption count will scale up to this value automatically.")] + [SerializeField] + private byte _maximumConsumeCount = 4; + /// + /// Maximum number of excessive replicates which can be consumed per tick. Consumption count will scale up to this value automatically. + /// + internal byte GetMaximumConsumeCount() => _maximumConsumeCount; + + /// + /// + /// + [Tooltip("Maximum number of past inputs which may send.")] + [Range(MINIMUM_PAST_INPUTS, MAXIMUM_PAST_INPUTS)] + [SerializeField] + private byte _redundancyCount = 3; + /// + /// Maximum number of past inputs which may send and resend redundancy. + /// +#if UNITY_WEBGL +//WebGL uses reliable so no reason to use redundancy. + internal byte GetRedundancyCount() => 1; +#else + internal byte GetRedundancyCount() => _redundancyCount; +#endif + /// + /// True to allow clients to use predicted spawning. While true, each NetworkObject prefab you wish to predicted spawn must be marked as to allow this feature. + /// + internal bool GetAllowPredictedSpawning() => _allowPredictedSpawning; + [Tooltip("True to allow clients to use predicted spawning and despawning. While true, each NetworkObject prefab you wish to predicted spawn must be marked as to allow this feature.")] + [SerializeField] + private bool _allowPredictedSpawning = false; + /// + /// + /// + [Tooltip("Maximum number of Ids to reserve on clients for predicted spawning. Higher values will allow clients to send more predicted spawns per second but may reduce availability of ObjectIds with high player counts.")] + [Range(1, 100)] + [SerializeField] + private byte _reservedObjectIds = 15; + /// + /// Maximum number of Ids to reserve on clients for predicted spawning. Higher values will allow clients to send more predicted spawns per second but may reduce availability of ObjectIds with high player counts. + /// + /// + internal byte GetReservedObjectIds() => _reservedObjectIds; + #endregion + + #region Private. + /// + /// Number of active predicted rigidbodies. + /// + [System.NonSerialized] + private HashSet _rigidbodies = new HashSet(); + /// + /// Cache to remove null entries from _rigidbodies. + /// + [System.NonSerialized] + private HashSet _componentCache = new HashSet(); + /// + /// NetworkManager used with this. + /// + private NetworkManager _networkManager; + /// + /// Scenes which are currently replaying prediction. + /// + private HashSet _replayingScenes = new HashSet(new SceneHandleEqualityComparer()); +#endregion + +#region Const. + /// + /// Minimum number of past inputs which can be sent. + /// + private const byte MINIMUM_PAST_INPUTS = 2; + /// + /// Maximum number of past inputs which can be sent. + /// + internal const byte MAXIMUM_PAST_INPUTS = 15; + /// + /// Minimum amount of replicate queue size. + /// + private const ushort MINIMUM_REPLICATE_QUEUE_SIZE = 10; + /// + /// Maxmimum amount of replicate queue size. + /// + private const ushort MAXIMUM_REPLICATE_QUEUE_SIZE = 500; +#endregion + + private void OnEnable() + { + UnityEngine.SceneManagement.SceneManager.sceneUnloaded += SceneManager_sceneUnloaded; + } + + private void OnDisable() + { + UnityEngine.SceneManagement.SceneManager.sceneUnloaded -= SceneManager_sceneUnloaded; + } + + internal void InitializeOnce_Internal(NetworkManager manager) + { + _networkManager = manager; + _networkManager.ClientManager.OnClientConnectionState += ClientManager_OnClientConnectionState; + } + + /// + /// Called after the local client connection state changes. + /// + private void ClientManager_OnClientConnectionState(ClientConnectionStateArgs obj) + { + if (obj.ConnectionState != LocalConnectionState.Started) + _replayingScenes.Clear(); + _isReplaying = false; + + } + + + /// + /// Called before and after server sends a reconcile. + /// + /// True if before the reconcile is sent. + internal void InvokeServerReconcile(NetworkBehaviour caller, bool before) + { + if (before) + OnPreServerReconcile?.Invoke(caller); + else + OnPostServerReconcile?.Invoke(caller); + } + + /// + /// Increases Rigidbodies count by 1. + /// + [APIExclude] + public void AddRigidbodyCount(UnityEngine.Component c) + { + _rigidbodies.Add(c); + } + + /// + /// Dencreases Rigidbodies count by 1. + /// + [APIExclude] + public void RemoveRigidbodyCount(UnityEngine.Component c) + { + bool removed = _rigidbodies.Remove(c); + /* If remove failed the rigidbodies may need to be rebuild. + * This might happen when an object is destroyed as + * the referenced is passed. Could be any number of things + * but it seems to occur frequently enough in Unity, + * especially when testing in editor. + * + * This operation is not ideal in the hot path but + * the odds of it happening are pretty slim and + * it ensures stability against user error. */ + if (!removed) + { + //Cannt remove null entries from a hashset so have to rebuild. + _componentCache.Clear(); + foreach (UnityEngine.Component item in _rigidbodies) + { + if (item != null) + _componentCache.Add(item); + } + + //Apply to rigidbodies. + _rigidbodies.Clear(); + foreach (UnityEngine.Component item in _componentCache) + _rigidbodies.Add(item); + } + } + + + /// + /// Invokes OnPre/PostReconcile events. + /// Internal use. + /// + [APIExclude] + [CodegenMakePublic] //To internal. + public void InvokeOnReconcile_Internal(NetworkBehaviour nb, bool before) + { + nb.IsReconciling = before; + if (before) + OnPreReconcile?.Invoke(nb); + else + OnPostReconcile?.Invoke(nb); + } + + /// + /// Invokes OnReplicateReplay. + /// Internal use. + /// + [APIExclude] + [CodegenMakePublic] //To internal. + public void InvokeOnReplicateReplay_Internal(UnityScene scene, uint tick, PhysicsScene ps, PhysicsScene2D ps2d, bool before) + { + _isReplaying = before; + if (before) + { + _replayingScenes.Add(scene); + OnPreReplicateReplay?.Invoke(tick, ps, ps2d); + } + else + { + _replayingScenes.Remove(scene); + OnPostReplicateReplay?.Invoke(tick, ps, ps2d); + } + } + + + /// + /// Called when a scene unloads. + /// + /// + private void SceneManager_sceneUnloaded(UnityScene s) + { + _replayingScenes.Remove(s); + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Prediction/PredictionManager.cs.meta b/Assets/FishNet/Runtime/Managing/Prediction/PredictionManager.cs.meta new file mode 100644 index 0000000..a2acd01 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Prediction/PredictionManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e08bb003fce297d4086cf8cba5aa459a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened.meta b/Assets/FishNet/Runtime/Managing/Scened.meta new file mode 100644 index 0000000..3e495f2 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 212ba5e93fab23e45a461067dc0bf611 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/Broadcast.meta b/Assets/FishNet/Runtime/Managing/Scened/Broadcast.meta new file mode 100644 index 0000000..0704a09 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/Broadcast.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8eb071c832ae3664c9b1701b52872513 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/Broadcast/SceneBroadcasts.cs b/Assets/FishNet/Runtime/Managing/Scened/Broadcast/SceneBroadcasts.cs new file mode 100644 index 0000000..5ce7c3b --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/Broadcast/SceneBroadcasts.cs @@ -0,0 +1,38 @@ +using FishNet.Broadcast; +using FishNet.Documenting; + +namespace FishNet.Managing.Scened +{ + + /// + /// Sent when there are starting scenes for the client to load. + /// + public struct EmptyStartScenesBroadcast : IBroadcast { } + /// + /// Sent to clients to load networked scenes. + /// + [APIExclude] + public struct LoadScenesBroadcast : IBroadcast + { + public LoadQueueData QueueData; + } + + /// + /// Sent to clients to unload networked scenes. + /// + [APIExclude] + public struct UnloadScenesBroadcast : IBroadcast + { + public UnloadQueueData QueueData; + } + + /// + /// Sent to server to indicate which scenes a client has loaded. + /// + [APIExclude] + public struct ClientScenesLoadedBroadcast : IBroadcast + { + public SceneLookupData[] SceneLookupDatas; + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Scened/Broadcast/SceneBroadcasts.cs.meta b/Assets/FishNet/Runtime/Managing/Scened/Broadcast/SceneBroadcasts.cs.meta new file mode 100644 index 0000000..1c271ef --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/Broadcast/SceneBroadcasts.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 698a94b4f8664ac4ab108deae0ba3b7c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/DefaultSceneProcessor.cs b/Assets/FishNet/Runtime/Managing/Scened/DefaultSceneProcessor.cs new file mode 100644 index 0000000..df5ef1e --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/DefaultSceneProcessor.cs @@ -0,0 +1,155 @@ +using System.Collections.Generic; +using UnityEngine; +using UnitySceneManager = UnityEngine.SceneManagement.SceneManager; +using UnityScene = UnityEngine.SceneManagement.Scene; +using System.Collections; + +namespace FishNet.Managing.Scened +{ + + public class DefaultSceneProcessor : SceneProcessorBase + { + #region Private. + /// + /// Currently active loading AsyncOperations. + /// + protected List LoadingAsyncOperations = new List(); + /// + /// A collection of scenes used both for loading and unloading. + /// + protected List Scenes = new List(); + /// + /// Current AsyncOperation being processed. + /// + protected AsyncOperation CurrentAsyncOperation; + #endregion + + /// + /// Called when scene loading has begun. + /// + public override void LoadStart(LoadQueueData queueData) + { + base.LoadStart(queueData); + ResetValues(); + } + + public override void LoadEnd(LoadQueueData queueData) + { + base.LoadEnd(queueData); + ResetValues(); + } + + /// + /// Resets values for a fresh load or unload. + /// + private void ResetValues() + { + CurrentAsyncOperation = null; + LoadingAsyncOperations.Clear(); + } + + /// + /// Called when scene unloading has begun within an unload operation. + /// + /// + public override void UnloadStart(UnloadQueueData queueData) + { + base.UnloadStart(queueData); + Scenes.Clear(); + } + + /// + /// Begin loading a scene using an async method. + /// + /// Scene name to load. + public override void BeginLoadAsync(string sceneName, UnityEngine.SceneManagement.LoadSceneParameters parameters) + { + AsyncOperation ao = UnitySceneManager.LoadSceneAsync(sceneName, parameters); + LoadingAsyncOperations.Add(ao); + + CurrentAsyncOperation = ao; + CurrentAsyncOperation.allowSceneActivation = false; + } + + /// + /// Begin unloading a scene using an async method. + /// + /// Scene name to unload. + public override void BeginUnloadAsync(UnityScene scene) + { + CurrentAsyncOperation = UnitySceneManager.UnloadSceneAsync(scene); + } + + /// + /// Returns if a scene load or unload percent is done. + /// + /// + public override bool IsPercentComplete() + { + return (GetPercentComplete() >= 0.9f); + } + + /// + /// Returns the progress on the current scene load or unload. + /// + /// + public override float GetPercentComplete() + { + return (CurrentAsyncOperation == null) ? 1f : CurrentAsyncOperation.progress; + } + + /// + /// Adds a loaded scene. + /// + /// Scene loaded. + public override void AddLoadedScene(UnityScene scene) + { + base.AddLoadedScene(scene); + Scenes.Add(scene); + } + + /// + /// Returns scenes which were loaded during a load operation. + /// + public override List GetLoadedScenes() + { + return Scenes; + } + + /// + /// Activates scenes which were loaded. + /// + public override void ActivateLoadedScenes() + { + foreach (AsyncOperation ao in LoadingAsyncOperations) + ao.allowSceneActivation = true; + } + + /// + /// Returns if all asynchronized tasks are considered IsDone. + /// + /// + public override IEnumerator AsyncsIsDone() + { + bool notDone; + do + { + notDone = false; + foreach (AsyncOperation ao in LoadingAsyncOperations) + { + + if (!ao.isDone) + { + notDone = true; + break; + } + } + yield return null; + } while (notDone); + + yield break; + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Scened/DefaultSceneProcessor.cs.meta b/Assets/FishNet/Runtime/Managing/Scened/DefaultSceneProcessor.cs.meta new file mode 100644 index 0000000..086a4ba --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/DefaultSceneProcessor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0c6eacaa60569d947b383df03fff1ea3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/Events.meta b/Assets/FishNet/Runtime/Managing/Scened/Events.meta new file mode 100644 index 0000000..8c02699 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/Events.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 09823f70d9231d34f91d88d11b937758 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/Events/ClientPresenceChangeEventArgs.cs b/Assets/FishNet/Runtime/Managing/Scened/Events/ClientPresenceChangeEventArgs.cs new file mode 100644 index 0000000..440613c --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/Events/ClientPresenceChangeEventArgs.cs @@ -0,0 +1,34 @@ +using FishNet.Connection; +using UnityEngine.SceneManagement; + +namespace FishNet.Managing.Scened +{ + /// + /// Data container about a scene presence change for a client. + /// + public struct ClientPresenceChangeEventArgs + { + + /// + /// Scene on the server which the client's presence has changed. + /// + public Scene Scene; + /// + /// Connection to client. + /// + public NetworkConnection Connection; + /// + /// True if the client was added to the scene, false is removed. + /// + public bool Added; + + internal ClientPresenceChangeEventArgs(Scene scene, NetworkConnection conn, bool added) + { + Scene = scene; + Connection = conn; + Added = added; + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Scened/Events/ClientPresenceChangeEventArgs.cs.meta b/Assets/FishNet/Runtime/Managing/Scened/Events/ClientPresenceChangeEventArgs.cs.meta new file mode 100644 index 0000000..e250e89 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/Events/ClientPresenceChangeEventArgs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fa91039d4ab1c6445af72881af122b0a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/Events/LoadSceneEventArgs.cs b/Assets/FishNet/Runtime/Managing/Scened/Events/LoadSceneEventArgs.cs new file mode 100644 index 0000000..304fcd5 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/Events/LoadSceneEventArgs.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; +using UnityEngine.SceneManagement; + +namespace FishNet.Managing.Scened +{ + /// + /// Data container about a scene load start. + /// + public struct SceneLoadStartEventArgs + { + /// + /// Queue data used by the current scene action. + /// + public readonly LoadQueueData QueueData; + + internal SceneLoadStartEventArgs(LoadQueueData lqd) + { + QueueData = lqd; + } + } + + + /// + /// Data container about a scene load percent change. + /// + public struct SceneLoadPercentEventArgs + { + /// + /// Queue data used by the current scene action. + /// + public readonly LoadQueueData QueueData; + /// + /// Percentage of change completion. 1f is equal to 100% complete. + /// + public readonly float Percent; + + internal SceneLoadPercentEventArgs(LoadQueueData lqd, float percent) + { + QueueData = lqd; + Percent = percent; + } + } + + + /// + /// Data container about a scene load end. + /// + public struct SceneLoadEndEventArgs + { + /// + /// Queue data used by the current scene action. + /// + public readonly LoadQueueData QueueData; + /// + /// Scenes which were loaded. + /// + public readonly Scene[] LoadedScenes; + /// + /// Scenes which were skipped because they were already loaded. + /// + public readonly string[] SkippedSceneNames; + /// + /// Scenes which were unloaded. + /// + public readonly string[] UnloadedSceneNames; + + internal SceneLoadEndEventArgs(LoadQueueData lqd, string[] skipped, Scene[] loaded, string[] unloadedSceneNames) + { + QueueData = lqd; + SkippedSceneNames = skipped; + LoadedScenes = loaded; + UnloadedSceneNames = unloadedSceneNames; + } + + + } + +} diff --git a/Assets/FishNet/Runtime/Managing/Scened/Events/LoadSceneEventArgs.cs.meta b/Assets/FishNet/Runtime/Managing/Scened/Events/LoadSceneEventArgs.cs.meta new file mode 100644 index 0000000..58c3671 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/Events/LoadSceneEventArgs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 86278568f8087de49b0908f148501993 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/Events/UnloadSceneEventArgs.cs b/Assets/FishNet/Runtime/Managing/Scened/Events/UnloadSceneEventArgs.cs new file mode 100644 index 0000000..2178873 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/Events/UnloadSceneEventArgs.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using UnityEngine.SceneManagement; + +namespace FishNet.Managing.Scened +{ + + /// + /// Data container about a scene unload start. + /// + public struct SceneUnloadStartEventArgs + { + /// + /// Queue data used by the current scene action. + /// + public readonly UnloadQueueData QueueData; + + internal SceneUnloadStartEventArgs(UnloadQueueData sqd) + { + QueueData = sqd; + } + } + + /// + /// Data container about a scene unload end. + /// + public struct SceneUnloadEndEventArgs + { + /// + /// Queue data used by the current scene action. + /// + public readonly UnloadQueueData QueueData; + /// + /// Handles of scenes which were successfully unloaded. + /// + [Obsolete("Use UnloadedScenesV2")] + public int[] UnloadedSceneHandles; + /// + /// Names of scenes which were successfully unloaded. + /// + [Obsolete("Use UnloadedScenesV2")] + public string[] UnloadedSceneNames; + + /// + /// Scenes which were successfully unloaded. + /// This collection may be populated with empty scenes depending on engine version. + /// + public List UnloadedScenes; + /// + /// Unloaded scenes with names and handles cached. + /// This will be renamed as UnloadedScenes in Fish-Networking version 4. + /// + public List UnloadedScenesV2; + + internal SceneUnloadEndEventArgs(UnloadQueueData sqd, List unloadedScenes, List newUnloadedScenes) + { + QueueData = sqd; + UnloadedScenes = unloadedScenes; + UnloadedScenesV2 = newUnloadedScenes; + +#pragma warning disable CS0618 // Type or member is obsolete + UnloadedSceneNames = new string[newUnloadedScenes.Count]; + UnloadedSceneHandles = new int[newUnloadedScenes.Count]; + for (int i = 0; i < newUnloadedScenes.Count; i++) + { + UnloadedSceneNames[i] = newUnloadedScenes[i].Name; + UnloadedSceneHandles[i] = newUnloadedScenes[i].Handle; + } +#pragma warning restore CS0618 + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Scened/Events/UnloadSceneEventArgs.cs.meta b/Assets/FishNet/Runtime/Managing/Scened/Events/UnloadSceneEventArgs.cs.meta new file mode 100644 index 0000000..da3c534 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/Events/UnloadSceneEventArgs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2c24765fea85b564aa331b529f324f92 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas.meta b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas.meta new file mode 100644 index 0000000..ad45a3a --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: beefc84827a4af141aa1b326fca9084f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadOptions.cs b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadOptions.cs new file mode 100644 index 0000000..95cb085 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadOptions.cs @@ -0,0 +1,39 @@ +using UnityEngine.SceneManagement; + +namespace FishNet.Managing.Scened +{ + /// + /// Settings to apply when loading a scene. + /// + public class LoadOptions + { + /// + /// True if to automatically unload the loaded scenes when they are no longer being used by clients. This field only applies to scenes loaded for connections, not globally loaded scenes. + /// + [System.NonSerialized] + public bool AutomaticallyUnload = true; + /// + /// False if to only load scenes which are not yet loaded. When true a scene may load multiple times; this is known as scene stacking. Only the server is able to stack scenes; clients will load a single instance. Global scenes cannot be stacked. + /// + [System.NonSerialized] + public bool AllowStacking; + /// + /// LocalPhysics mode to use when loading this scene. Generally this will only be used when applying scene stacking. Only used by the server. + /// https://docs.unity3d.com/ScriptReference/SceneManagement.LocalPhysicsMode.html + /// + [System.NonSerialized] + public LocalPhysicsMode LocalPhysics = LocalPhysicsMode.None; + /// + /// True to reload a scene if it's already loaded. + /// This does not function yet. + /// + [System.Obsolete("This feature is not functional yet but will be at a later release.")] + public bool ReloadScenes; + /// + /// True if scenes should be loaded using addressables. This field only exists for optional use so the user may know if their queue data is using addressables. + /// + public bool Addressables; + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadOptions.cs.meta b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadOptions.cs.meta new file mode 100644 index 0000000..3ca0ef9 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadOptions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1614453d3786b2a4eb18b69297da7dc9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadParams.cs b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadParams.cs new file mode 100644 index 0000000..0c62f63 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadParams.cs @@ -0,0 +1,19 @@ +namespace FishNet.Managing.Scened +{ + /// + /// Additional user-crafted data which can be included in scene load callbacks. + /// + public class LoadParams + { + /// + /// Objects which are included in callbacks on the server when loading a scene. Can be useful for including unique information about the scene, such as match id. These are not sent to clients; use ClientParams for this. + /// + [System.NonSerialized] + public object[] ServerParams = new object[0]; + /// + /// Bytes which are sent to clients during scene loads. Can contain any information. + /// + public byte[] ClientParams = new byte[0]; + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadParams.cs.meta b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadParams.cs.meta new file mode 100644 index 0000000..ce62050 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadParams.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d8b395f67f61b4e45830a70289a1901d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadQueueData.cs b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadQueueData.cs new file mode 100644 index 0000000..c2300f6 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadQueueData.cs @@ -0,0 +1,51 @@ +using FishNet.Connection; +using FishNet.Utility.Constant; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo(UtilityConstants.GENERATED_ASSEMBLY_NAME)] +namespace FishNet.Managing.Scened +{ + + + /// + /// Data generated when loading a scene. + /// + public class LoadQueueData + { + /// + /// Clients which receive this SceneQueueData. If Networked, all clients do. If Connections, only the specified Connections do. + /// + [System.NonSerialized] + public SceneScopeType ScopeType; + /// + /// Connections to load scenes for. Only valid on the server and when ScopeType is Connections. + /// + [System.NonSerialized] + public NetworkConnection[] Connections = new NetworkConnection[0]; + /// + /// SceneLoadData to use. + /// + public SceneLoadData SceneLoadData = null; + /// + /// Current global scenes. + /// + public string[] GlobalScenes = new string[0]; + /// + /// True if to iterate this queue data as server. + /// + [System.NonSerialized] + public readonly bool AsServer; + + public LoadQueueData() { } + internal LoadQueueData(SceneScopeType scopeType, NetworkConnection[] conns, SceneLoadData sceneLoadData, string[] globalScenes, bool asServer) + { + ScopeType = scopeType; + Connections = conns; + SceneLoadData = sceneLoadData; + GlobalScenes = globalScenes; + AsServer = asServer; + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadQueueData.cs.meta b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadQueueData.cs.meta new file mode 100644 index 0000000..d58489f --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadQueueData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8fb4183af628f754b800dfdbb1ba9bf0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/ReplaceOption.cs b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/ReplaceOption.cs new file mode 100644 index 0000000..96a3800 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/ReplaceOption.cs @@ -0,0 +1,25 @@ + +namespace FishNet.Managing.Scened +{ + /// + /// How to replace scenes when loading. + /// + public enum ReplaceOption : byte + { + /// + /// Replace all scenes, online and offline. + /// + All, + /// + /// Only replace scenes loaded using the SceneManager. + /// + OnlineOnly, + /// + /// Do not replace any scenes, additional scenes will be loaded as additive. + /// + None + } + + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/ReplaceOption.cs.meta b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/ReplaceOption.cs.meta new file mode 100644 index 0000000..c1ab805 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/ReplaceOption.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cb8e2c0fe3b9d3344a05810936861555 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneLoadData.cs b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneLoadData.cs new file mode 100644 index 0000000..06eb39f --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneLoadData.cs @@ -0,0 +1,201 @@ +using FishNet.Object; +using FishNet.Serializing.Helping; +using System.Collections.Generic; +using System.IO; +using UnityEngine.SceneManagement; + +namespace FishNet.Managing.Scened +{ + /// + /// Data about which scenes to load. + /// + public class SceneLoadData + { + /// + /// When specified this scene will be set as the active scene after loading occurs. + /// + public SceneLookupData PreferredActiveScene = null; + /// + /// SceneLookupData for each scene to load. + /// + public SceneLookupData[] SceneLookupDatas = new SceneLookupData[0]; + /// + /// NetworkObjects to move to the new scenes. Objects will be moved to the first scene. + /// + public NetworkObject[] MovedNetworkObjects = new NetworkObject[0]; + /// + /// How to replace current scenes with new ones. When replacing scenes the first scene loaded will be set as the active scene, and the rest additive. + /// + public ReplaceOption ReplaceScenes = ReplaceOption.None; + /// + /// Parameters which may be set and will be included in load callbacks. + /// + public LoadParams Params = new LoadParams(); + /// + /// Additional options to use for loaded scenes. + /// + public LoadOptions Options = new LoadOptions(); + + public SceneLoadData() { } + /// + /// + /// + /// Scene to load. + public SceneLoadData(Scene scene) : this(new Scene[] { scene }, null) { } + /// + /// + /// + /// Scene to load by name. + public SceneLoadData(string sceneName) : this(new string[] { sceneName }, null) { } + /// + /// + /// + /// Scene to load by handle. + public SceneLoadData(int sceneHandle) : this(new int[] { sceneHandle }, null) { } + /// + /// + /// + /// Scene to load by handle. + /// Scene to load by name. + public SceneLoadData(int sceneHandle, string sceneName) : this(new SceneLookupData(sceneHandle, sceneName)) { } + /// + /// + /// + /// Scene to load by SceneLookupData. + public SceneLoadData(SceneLookupData sceneLookupData) : this(new SceneLookupData[] { sceneLookupData }) { } + /// + /// + /// + /// Scenes to load. + public SceneLoadData(List scenes) : this(scenes.ToArray(), null) { } + /// + /// + /// + /// Scenes to load by name. + public SceneLoadData(List sceneNames) : this(sceneNames.ToArray(), null) { } + /// + /// + /// + /// Scenes to load by handle. + public SceneLoadData(List sceneHandles) : this(sceneHandles.ToArray(), null) { } + /// + /// + /// + /// Scenes to load. + public SceneLoadData(Scene[] scenes) : this(scenes, null) { } + /// + /// + /// + /// Scenes to load by name. + public SceneLoadData(string[] sceneNames) : this(sceneNames, null) { } + /// + /// + /// + /// Scenes to load by handle. + public SceneLoadData(int[] sceneHandles) : this(sceneHandles, null) { } + /// + /// + /// + /// Scenes to load by SceneLookupDatas. + public SceneLoadData(SceneLookupData[] sceneLookupDatas) : this(sceneLookupDatas, null) { } + + /// + /// + /// + /// Scene to load. + /// NetworkObjects to move to the first specified scene. + public SceneLoadData(Scene scene, NetworkObject[] movedNetworkObjects) + { + SceneLookupData data = SceneLookupData.CreateData(scene); + Construct(new SceneLookupData[] { data }, movedNetworkObjects); + } + /// + /// + /// + /// Scenes to load. + /// NetworkObjects to move to the first specified scene. + public SceneLoadData(Scene[] scenes, NetworkObject[] movedNetworkObjects) + { + SceneLookupData[] datas = SceneLookupData.CreateData(scenes); + Construct(datas, movedNetworkObjects); + } + /// + /// + /// + /// Scenes to load by Name. + /// NetworkObjects to move to the first specified scene. + public SceneLoadData(string[] sceneNames, NetworkObject[] movedNetworkObjects) + { + SceneLookupData[] datas = SceneLookupData.CreateData(sceneNames); + Construct(datas, movedNetworkObjects); + } + /// + /// + /// + /// Scenes to load by handle. + /// NetworkObjects to move to the first specified scene. + public SceneLoadData(int[] sceneHandles, NetworkObject[] movedNetworkObjects) + { + SceneLookupData[] datas = SceneLookupData.CreateData(sceneHandles); + Construct(datas, movedNetworkObjects); + } + /// + /// + /// + /// Scenes to load by SceneLookupDatas. + /// NetworkObjects to move to the first specified scene. + public SceneLoadData(SceneLookupData[] sceneLookupDatas, NetworkObject[] movedNetworkObjects) + { + Construct(sceneLookupDatas, movedNetworkObjects); + } + + /// + /// Called at the end of every constructor. + /// + private void Construct(SceneLookupData[] datas, NetworkObject[] movedNetworkObjects) + { + SceneLookupDatas = datas; + if (movedNetworkObjects == null) + movedNetworkObjects = new NetworkObject[0]; + MovedNetworkObjects = movedNetworkObjects; + } + + /// + /// Gets the first Scene in SceneLookupDatas. + /// + /// + public Scene GetFirstLookupScene() + { + foreach (SceneLookupData sld in SceneLookupDatas) + { + Scene result = sld.GetScene(out _); + if (!string.IsNullOrEmpty(result.name)) + return result; + } + + return default; + } + + + /// + /// Returns if any data is invalid, such as null entries. + /// + /// + internal bool DataInvalid() + { + //Null values. + if (Params == null || MovedNetworkObjects == null || SceneLookupDatas == null || + Options == null) + return true; + //No lookups. + if (SceneLookupDatas.Length == 0) + return true; + + return false; + } + + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneLoadData.cs.meta b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneLoadData.cs.meta new file mode 100644 index 0000000..e6bb59a --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneLoadData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ecd4065158ab62047a074c594f245d90 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneScopeTypes.cs b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneScopeTypes.cs new file mode 100644 index 0000000..91087e0 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneScopeTypes.cs @@ -0,0 +1,18 @@ +namespace FishNet.Managing.Scened +{ + /// + /// Type of scopes for a scene load or unload. + /// + public enum SceneScopeType : byte + { + /// + /// Scene action occured for all clients. + /// + Global = 0, + /// + /// Scene action occurred for specified clients. + /// + Connections = 1 + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneScopeTypes.cs.meta b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneScopeTypes.cs.meta new file mode 100644 index 0000000..9baf271 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneScopeTypes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2be621eb04519a14eb2297a666b1bc2c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneUnloadData.cs b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneUnloadData.cs new file mode 100644 index 0000000..d17a63b --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneUnloadData.cs @@ -0,0 +1,116 @@ +using System.Collections.Generic; +using UnityEngine.SceneManagement; + +namespace FishNet.Managing.Scened +{ + + /// + /// Data about which scenes to unload. + /// + public class SceneUnloadData + { + /// + /// When specified this scene will be set as the active scene after unloading occurs. + /// + public SceneLookupData PreferredActiveScene = null; + /// + /// SceneLookupData for each scene to load. + /// + public SceneLookupData[] SceneLookupDatas = new SceneLookupData[0]; + /// + /// Parameters which may be set and will be included in load callbacks. + /// + public UnloadParams Params = new UnloadParams(); + /// + /// Additional options to use for loaded scenes. + /// + public UnloadOptions Options = new UnloadOptions(); + + /// + /// + /// + public SceneUnloadData() { } + /// + /// + /// + /// Scene to unload. + public SceneUnloadData(Scene scene) : this(new Scene[] { scene }) { } + /// + /// + /// + /// Scene to unload by name. + public SceneUnloadData(string sceneName) : this(new string[] { sceneName }) { } + /// + /// + /// + /// Scene to unload by handle. + public SceneUnloadData(int sceneHandle) : this(new int[] { sceneHandle }) { } + /// + /// + /// + /// Scenes to unload. + public SceneUnloadData(List scenes) : this(scenes.ToArray()) { } + /// + /// + /// + /// Scenes to unload by names. + public SceneUnloadData(List sceneNames) : this(sceneNames.ToArray()) { } + /// + /// + /// + /// Scenes to unload by handles. + public SceneUnloadData(List sceneHandles) : this(sceneHandles.ToArray()) { } + /// + /// + /// + /// Scenes to unload. + public SceneUnloadData(Scene[] scenes) + { + SceneLookupDatas = SceneLookupData.CreateData(scenes); + } + /// + /// + /// + /// Scenes to unload by names. + public SceneUnloadData(string[] sceneNames) + { + SceneLookupDatas = SceneLookupData.CreateData(sceneNames); + } + /// + /// + /// + /// Scenes to unload by handles. + public SceneUnloadData(int[] sceneHandles) + { + SceneLookupDatas = SceneLookupData.CreateData(sceneHandles); + } + /// + /// + /// + /// Scenes to unload by SceneLookupDatas. + public SceneUnloadData(SceneLookupData[] sceneLookupDatas) + { + SceneLookupDatas = sceneLookupDatas; + } + + + /// + /// Returns if any data is invalid, such as null entries. + /// + /// + internal bool DataInvalid() + { + //Null values. + if (Params == null || SceneLookupDatas == null || + Options == null) + return true; + //No lookups. + if (SceneLookupDatas.Length == 0) + return true; + + return false; + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneUnloadData.cs.meta b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneUnloadData.cs.meta new file mode 100644 index 0000000..a4a3d0d --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneUnloadData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 77cbfeea232e4ab44a4315b003ce6742 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadOptions.cs b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadOptions.cs new file mode 100644 index 0000000..8224759 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadOptions.cs @@ -0,0 +1,36 @@ + +namespace FishNet.Managing.Scened +{ + /// + /// Settings to apply when loading a scene. + /// + public class UnloadOptions + { + /// + /// Conditions to unloading a scene on the server. + /// + public enum ServerUnloadMode + { + /// + /// Unloads the scene if no more connections are within it. + /// + UnloadUnused = 0, + /// + /// Unloads scenes for connections but keeps scene loaded on server even if no connections are within it. + /// + KeepUnused = 1, + } + + /// + /// How to unload scenes on the server. UnloadUnused will unload scenes which have no more clients in them. KeepUnused will not unload a scene even when empty. ForceUnload will unload a scene regardless of if clients are still connected to it. + /// + [System.NonSerialized] + public ServerUnloadMode Mode = ServerUnloadMode.UnloadUnused; + /// + /// True if scenes should be loaded using addressables. This field only exists for optional use so the user may know if their queue data is using addressables. + /// + public bool Addressables; + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadOptions.cs.meta b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadOptions.cs.meta new file mode 100644 index 0000000..68dcbd5 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadOptions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3de31d76de313bc49aefba61135fdffc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadParams.cs b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadParams.cs new file mode 100644 index 0000000..ca23c63 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadParams.cs @@ -0,0 +1,19 @@ +namespace FishNet.Managing.Scened +{ + /// + /// Additional user-crafted data which can be included in scene unload callbacks. + /// + public class UnloadParams + { + /// + /// Objects which are included in callbacks on the server when unloading a scene. Can be useful for including unique information about the scene, such as match id. These are not sent to clients; use ClientParams for this. + /// + [System.NonSerialized] + public object[] ServerParams = new object[0]; + /// + /// Bytes which are sent to clients during scene unloads. Can contain any information. + /// + public byte[] ClientParams = new byte[0]; + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadParams.cs.meta b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadParams.cs.meta new file mode 100644 index 0000000..90ef393 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadParams.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3bba3fbbe6ffbae4bb18d50c8c9b3b30 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadQueueData.cs b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadQueueData.cs new file mode 100644 index 0000000..f53bee6 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadQueueData.cs @@ -0,0 +1,53 @@ +using FishNet.Connection; +using FishNet.Utility.Constant; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo(UtilityConstants.GENERATED_ASSEMBLY_NAME)] +namespace FishNet.Managing.Scened +{ + + /// + /// Data generated when unloading a scene. + /// + public class UnloadQueueData + { + /// + /// Clients which receive this SceneQueueData. If Networked, all clients do. If Connections, only the specified Connections do. + /// + [System.NonSerialized] + public readonly SceneScopeType ScopeType; + /// + /// Connections to unload scenes for. Only valid on the server and when ScopeType is Connections. + /// + [System.NonSerialized] + public NetworkConnection[] Connections; + /// + /// SceneUnloadData to use. + /// + public SceneUnloadData SceneUnloadData = null; + /// + /// Current global scenes. + /// + public string[] GlobalScenes = new string[0]; + /// + /// True if to iterate this queue data as server. + /// + [System.NonSerialized] + public readonly bool AsServer; + + public UnloadQueueData() { } + internal UnloadQueueData(SceneScopeType scopeType, NetworkConnection[] conns, SceneUnloadData sceneUnloadData, string[] globalScenes, bool asServer) + { + ScopeType = scopeType; + Connections = conns; + SceneUnloadData = sceneUnloadData; + GlobalScenes = globalScenes; + AsServer = asServer; + } + + + } + + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadQueueData.cs.meta b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadQueueData.cs.meta new file mode 100644 index 0000000..782ce32 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadQueueData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ea9c20d60381ea74f974fb87a7e78297 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/SceneLookupData.cs b/Assets/FishNet/Runtime/Managing/Scened/SceneLookupData.cs new file mode 100644 index 0000000..72d525d --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/SceneLookupData.cs @@ -0,0 +1,322 @@ +using FishNet.Managing.Logging; +using FishNet.Serializing.Helping; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace FishNet.Managing.Scened +{ + /// + /// Extensions for SceneLookupData. + /// + internal static class SceneLookupDataExtensions + { + /// + /// Returns Names from SceneLookupData. + /// + /// + /// + public static string[] GetNames(this SceneLookupData[] datas) + { + string[] names = new string[datas.Length]; + for (int i = 0; i < datas.Length; i++) + names[i] = datas[i].Name; + + return names; + } + } + + /// + /// Data container for looking up, loading, or unloading a scene. + /// + public class SceneLookupData + { + /// + /// Handle of the scene. If value is 0, then handle is not used. + /// + public int Handle; + /// + /// Name of the scene. + /// + public string Name = string.Empty; + /// + /// Returns the scene name without a directory path should one exist. + /// + public string NameOnly => System.IO.Path.GetFileNameWithoutExtension(Name); + /// + /// Returns if this data is valid for use. + /// Being valid does not mean that the scene exist, rather that there is enough data to try and lookup a scene. + /// + public bool IsValid => (Name != string.Empty || Handle != 0); + + #region Const + /// + /// String to display when scene data is invalid. + /// + private const string INVALID_SCENE = "One or more scene information entries contain invalid data and have been skipped."; + #endregion + + /// + /// + /// + public SceneLookupData() { } + /// + /// + /// + /// Scene to generate from. + public SceneLookupData(Scene scene) + { + Handle = scene.handle; + Name = scene.name; + } + /// + /// + /// + /// Scene name to generate from. + public SceneLookupData(string name) + { + Name = name; + } + /// + /// + /// + /// Scene handle to generate from. + public SceneLookupData(int handle) + { + Handle = handle; + } + /// + /// + /// + /// Scene handle to generate from. + /// Name to generate from if handle is 0. + public SceneLookupData(int handle, string name) + { + Handle = handle; + Name = name; + } + + #region Comparers. + public static bool operator ==(SceneLookupData sldA, SceneLookupData sldB) + { + //One is null while the other is not. + if ((sldA is null) != (sldB is null)) + return false; + + /*If here both are either null or have value. */ + if (!(sldA is null)) + return sldA.Equals(sldB); + else if (!(sldB is null)) + return sldB.Equals(sldA); + + //Fall through indicates both are null. + return true; + } + + public static bool operator !=(SceneLookupData sldA, SceneLookupData sldB) + { + //One is null while the other is not. + if ((sldA is null) != (sldB is null)) + return true; + + /*If here both are either null or have value. */ + if (!(sldA is null)) + return !sldA.Equals(sldB); + else if (!(sldB is null)) + return !sldB.Equals(sldA); + + //Fall through indicates both are null. + return true; + } + + public bool Equals(SceneLookupData sld) + { + //Comparing instanced against null. + if (sld is null) + return false; + + //True if both handles are empty. + bool bothHandlesEmpty = ( + (this.Handle == 0) && + (sld.Handle == 0) + ); + + //If both have handles and they match. + if (!bothHandlesEmpty && sld.Handle == this.Handle) + return true; + //If neither have handles and name matches. + else if (bothHandlesEmpty && sld.Name == this.Name) + return true; + + //Fall through. + return false; + } + + public override int GetHashCode() + { + int hashCode = 2053068273; + hashCode = hashCode * -1521134295 + Handle.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Name); + return hashCode; + } + + public override bool Equals(object obj) + { + return base.Equals(obj); + } + + public override string ToString() + { + return base.ToString(); + } + #endregion + + #region CreateData. + /// + /// Returns a new SceneLookupData. + /// + /// Scene to create from. + /// + public static SceneLookupData CreateData(Scene scene) => new SceneLookupData(scene); + /// + /// Returns a new SceneLookupData. + /// + /// Scene name to create from. + /// + public static SceneLookupData CreateData(string name) => new SceneLookupData(name); + /// + /// Returns a new SceneLookupData. + /// + /// Scene handle to create from. + /// + public static SceneLookupData CreateData(int handle) => new SceneLookupData(handle); + /// + /// Returns a SceneLookupData collection. + /// + /// Scenes to create from. + /// + public static SceneLookupData[] CreateData(List scenes) => CreateData(scenes.ToArray()); + /// + /// Returns a SceneLookupData collection. + /// + /// Scene names to create from. + /// + public static SceneLookupData[] CreateData(List names) => CreateData(names.ToArray()); + /// + /// Returns a SceneLookupData collection. + /// + /// Scene handles to create from. + /// + public static SceneLookupData[] CreateData(List handles) => CreateData(handles.ToArray()); + /// + /// Returns a SceneLookupData collection. + /// + /// Scenes to create from. + /// + public static SceneLookupData[] CreateData(Scene[] scenes) + { + bool invalidFound = false; + List result = new List(); + foreach (Scene item in scenes) + { + if (!item.IsValid()) + { + invalidFound = true; + continue; + } + + result.Add(CreateData(item)); + } + + if (invalidFound) + NetworkManager.StaticLogWarning(INVALID_SCENE); + + return result.ToArray(); + } + /// + /// Returns a SceneLookupData collection. + /// + /// Scene names to create from. + /// + public static SceneLookupData[] CreateData(string[] names) + { + bool invalidFound = false; + List result = new List(); + foreach (string item in names) + { + if (string.IsNullOrEmpty(item)) + { + invalidFound = true; + continue; + } + + string nameOnly = System.IO.Path.GetFileNameWithoutExtension(item); + result.Add(CreateData(nameOnly)); + } + + if (invalidFound) + NetworkManager.StaticLogWarning(INVALID_SCENE); + + return result.ToArray(); + } + /// + /// Returns a SceneLookupData collection. + /// + /// Scene handles to create from. + /// + public static SceneLookupData[] CreateData(int[] handles) + { + bool invalidFound = false; + List result = new List(); + foreach (int item in handles) + { + if (item == 0) + { + invalidFound = true; + continue; + } + + result.Add(CreateData(item)); + } + + if (invalidFound) + NetworkManager.StaticLogWarning(INVALID_SCENE); + + return result.ToArray(); + } + #endregion + + /// + /// Returns the first scene found using Handle or Name, preferring Handle. + /// + /// + /// True if scene was found by handle. Handle is always checked first. + public Scene GetScene(out bool foundByHandle) + { + foundByHandle = false; + + if (Handle == 0 && string.IsNullOrEmpty(Name)) + { + NetworkManager.StaticLogWarning("Scene handle and name is unset; scene cannot be returned."); + return default; + } + + Scene result = default; + + //Lookup my handle. + if (Handle != 0) + { + result = SceneManager.GetScene(Handle); + if (result.handle != 0) + foundByHandle = true; + } + + //If couldnt find handle try by string. + if (!foundByHandle) + result = SceneManager.GetScene(NameOnly); + + return result; + } + + } +} diff --git a/Assets/FishNet/Runtime/Managing/Scened/SceneLookupData.cs.meta b/Assets/FishNet/Runtime/Managing/Scened/SceneLookupData.cs.meta new file mode 100644 index 0000000..2f035f7 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/SceneLookupData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: df1ac9b164e75da46bc52f4dd4fe30ba +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/SceneManager.cs b/Assets/FishNet/Runtime/Managing/Scened/SceneManager.cs new file mode 100644 index 0000000..8bd1e10 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/SceneManager.cs @@ -0,0 +1,2138 @@ +using FishNet.Connection; +using FishNet.Managing.Client; +using FishNet.Managing.Logging; +using FishNet.Managing.Server; +using FishNet.Object; +using FishNet.Serializing.Helping; +using FishNet.Transporting; +using FishNet.Utility; +using FishNet.Utility.Extension; +using FishNet.Utility.Performance; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnitySceneManager = UnityEngine.SceneManagement.SceneManager; + +namespace FishNet.Managing.Scened +{ + /// + /// Handles loading, unloading, and scene visibility for clients. + /// + [DisallowMultipleComponent] + [AddComponentMenu("FishNet/Manager/SceneManager")] + public sealed class SceneManager : MonoBehaviour + { + #region Types. + internal enum LightProbeUpdateType + { + Asynchronous = 0, + BlockThread = 1, + Off = 2, + } + #endregion + + #region Public. + /// + /// Called after the active scene has been set, immediately after scene loads. This will occur before NetworkBehaviour callbacks run for the scene's objects. + /// The boolean will indicate if the scene set active was specified by the user. + /// + public event Action OnActiveSceneSet; + /// + /// Called when a client loads initial scenes after connecting. Boolean will be true if asServer. This will invoke even if the SceneManager is not used when the client completes fully connecting to the server. + /// + public event Action OnClientLoadedStartScenes; + /// + /// Called when a scene change queue has begun. This will only call if a scene has succesfully begun to load or unload. The queue may process any number of scene events. For example: if a scene is told to unload while a load is still in progress, then the unload will be placed in the queue. + /// + public event Action OnQueueStart; + /// + /// Called when the scene queue is emptied. + /// + public event Action OnQueueEnd; + /// + /// Called when a scene load starts. + /// + public event Action OnLoadStart; + /// + /// Called when completion percentage changes while loading a scene. Value is between 0f and 1f, while 1f is 100% done. Can be used for custom progress bars when loading scenes. + /// + public event Action OnLoadPercentChange; + /// + /// Called when a scene load ends. + /// + public event Action OnLoadEnd; + /// + /// Called when a scene unload starts. + /// + public event Action OnUnloadStart; + /// + /// Called when a scene unload ends. + /// + public event Action OnUnloadEnd; + /// + /// Called when a client presence changes within a scene, before the server rebuilds observers. + /// + public event Action OnClientPresenceChangeStart; + /// + /// Called when a client presence changes within a scene, after the server rebuilds observers. + /// + public event Action OnClientPresenceChangeEnd; + /// + /// Connections within each scene. + /// + public Dictionary> SceneConnections { get; private set; } = new Dictionary>(); + /// + /// + /// + [Tooltip("Script to handle addressables loading and unloading. This field may be blank if addressables are not being used.")] + [SerializeField] + private SceneProcessorBase _sceneProcessor; + /// + /// Script to handle addressables loading and unloading. This field may be blank if addressables are not being used. + /// + /// + public SceneProcessorBase GetSceneProcessor() => _sceneProcessor; + /// + /// Sets the SceneProcessor to use. + /// + /// + public void SetSceneProcessor(SceneProcessorBase value) => _sceneProcessor = value; + #endregion + + #region Internal. + /// + /// Called after the active scene has been set, immediately after scene loads. + /// + internal event Action OnActiveSceneSetInternal; + #endregion + + #region Serialized. + /// + /// How to update light probes after loading or unloading scenes. + /// + [Tooltip("How to update light probes after loading or unloading scenes.")] + [SerializeField] + private LightProbeUpdateType _lightProbeUpdating = LightProbeUpdateType.Asynchronous; + /// + /// True to move objects visible to clientHost that are within an unloading scene. This ensures the objects are despawned on the client side rather than when the scene is destroyed. + /// + [Tooltip("True to move objects visible to clientHost that are within an unloading scene. This ensures the objects are despawned on the client side rather than when the scene is destroyed.")] + [SerializeField] + private bool _moveClientHostObjects = true; + /// + /// True to automatically set active scenes when loading and unloading scenes. + /// + [Tooltip("True to automatically set active scenes when loading and unloading scenes.")] + [SerializeField] + private bool _setActiveScene = true; + #endregion + + #region Private. + /// + /// NetworkManager for this script. + /// + private NetworkManager _networkManager; + /// + /// ServerManager for this script. + /// + private ServerManager _serverManager => _networkManager.ServerManager; + /// + /// ClientManager for this script. + /// + private ClientManager _clientManager => _networkManager.ClientManager; + /// + /// Scenes which are currently loaded as networked scenes. All players should have networked scenes loaded. + /// + private string[] _globalScenes = new string[0]; + /// + /// Lastest SceneLoadData for a global load. + /// + private SceneLoadData _globalSceneLoadData = new SceneLoadData(); + /// + /// Scenes to load or unload, in order. + /// + private List _queuedOperations = new List(); + /// + /// Scenes which must be manually unloaded, even when emptied. + /// + private HashSet _manualUnloadScenes = new HashSet(); + /// + /// Scene containing moved objects when changing single scene. On client this will contain all objects moved until the server destroys them. + /// The network only sends spawn messages once per-client, per server side scene load. If a scene load is performed only for specific connections + /// then the server is not resetting their single scene, but rather the single scene for those connections only. Because of this, any objects + /// which are to be moved will not receive a second respawn message, as they are never destroyed on server, only on client. + /// While on server only this scene contains objects being moved temporarily, before being moved to the new scene. + /// + private Scene _movedObjectsScene; + /// + /// Scene containing objects awaiting to be destroyed by the client-host. + /// This is required when unloading scenes where the client-host has visibility. + /// Otherwise the objects would become destroyed when the scene unloads on the server + /// which would cause missing networkobjects on clients when receiving despawn messages. + /// + private Scene _delayedDestroyScene; + /// + /// A scene to be set as the active scene where there are no global scenes. + /// This is used to prevent connection scenes and MovedObjectsScene from becoming the active scene. + /// + private Scene _fallbackActiveScene; + /// + /// Becomes true when when a scene first successfully begins to load or unload. Value is reset to false when the scene queue is emptied. + /// + private bool _sceneQueueStartInvoked; + /// + /// Objects being moved from MovedObjects scene to another. + /// + private List _movingObjects = new List(); + /// + /// How many scene load confirmations the server is expecting from a client. + /// Unloads do not need to be checked because server does not require confirmation for those. + /// This is used to prevent attacks. + /// + private Dictionary _pendingClientSceneChanges = new Dictionary(); + #endregion + + #region Consts. + /// + /// String to use when scene data used to load is invalid. + /// + private const string INVALID_SCENELOADDATA = "One or more datas in SceneLoadData are invalid.This generally occurs when calling this method without specifying any scenes or when data fields are null."; + /// + /// String to use when scene data used to unload is invalid. + /// + private const string INVALID_SCENEUNLOADDATA = "One or more datas in SceneLoadData are invalid.This generally occurs when calling this method without specifying any scenes or when data fields are null."; + #endregion + + #region Unity callbacks and initialization. + private void Awake() + { + UnitySceneManager.sceneUnloaded += SceneManager_SceneUnloaded; + if (_sceneProcessor == null) + _sceneProcessor = gameObject.AddComponent(); + _sceneProcessor.Initialize(this); + } + + private void Start() + { + //No need to unregister since managers are on the same object. + _networkManager.ServerManager.OnRemoteConnectionState += ServerManager_OnRemoteConnectionState; + _networkManager.ServerManager.OnServerConnectionState += ServerManager_OnServerConnectionState; + _clientManager.RegisterBroadcast(OnLoadScenes); + _clientManager.RegisterBroadcast(OnUnloadScenes); + _serverManager.RegisterBroadcast(OnClientLoadedScenes); + _serverManager.RegisterBroadcast(OnServerEmptyStartScenes); + _clientManager.RegisterBroadcast(OnClientEmptyStartScenes); + } + + private void OnDestroy() + { + UnitySceneManager.sceneUnloaded -= SceneManager_SceneUnloaded; + } + + /// + /// Called when the server connection state changes. + /// + private void ServerManager_OnServerConnectionState(ServerConnectionStateArgs obj) + { + //If no servers are started. + if (!_networkManager.ServerManager.AnyServerStarted()) + ResetValues(); + + } + /// + /// Resets as if first use. + /// + private void ResetValues() + { + SceneConnections.Clear(); + _globalScenes = new string[0]; + _globalSceneLoadData = new SceneLoadData(); + _queuedOperations.Clear(); + _manualUnloadScenes.Clear(); + _sceneQueueStartInvoked = false; + _movingObjects.Clear(); + } + + /// + /// Called when a connection state changes for a remote client. + /// + private void ServerManager_OnRemoteConnectionState(NetworkConnection arg1, RemoteConnectionStateArgs arg2) + { + if (arg2.ConnectionState == RemoteConnectionState.Stopped) + ClientDisconnected(arg1); + } + + /// + /// Initializes this script for use. + /// + /// + internal void InitializeOnce_Internal(NetworkManager manager) + { + _networkManager = manager; + } + + /// + /// Received when a scene is unloaded. + /// + /// + private void SceneManager_SceneUnloaded(Scene scene) + { + if (!_networkManager.IsServer) + return; + + /* Remove any unloaded scenes from local variables. This shouldn't + * be needed if the user properly utilizes this scene manager, + * but just incase, we don't want a memory leak. */ + SceneConnections.Remove(scene); + _manualUnloadScenes.Remove(scene); + RemoveFromGlobalScenes(scene); + } + #endregion + + #region Initial synchronizing. + /// + /// Invokes OnClientLoadedStartScenes if connection just loaded start scenes. + /// + /// + private void TryInvokeLoadedStartScenes(NetworkConnection connection, bool asServer) + { + if (connection.SetLoadedStartScenes(asServer)) + OnClientLoadedStartScenes?.Invoke(connection, asServer); + } + + /// + /// Called when authenitcator has concluded a result for a connection. Boolean is true if authentication passed, false if failed. This invokes before OnClientAuthenticated so FishNet may run operations on authenticated clients before user code does. + /// + /// + internal void OnClientAuthenticated(NetworkConnection connection) + { + AddPendingLoad(connection); + + //No global scenes to load. + if (_globalScenes.Length == 0) + { + /* Invoke that client had loaded the default scenes immediately, + * since there are no scenes to load. */ + //OnClientLoadedScenes(connection, new ClientScenesLoadedBroadcast()); + //Tell the client there are no scenes to load. + EmptyStartScenesBroadcast msg = new EmptyStartScenesBroadcast(); + connection.Broadcast(msg); + } + else + { + SceneLoadData sld = new SceneLoadData(_globalScenes); + sld.Params = _globalSceneLoadData.Params; + sld.Options = _globalSceneLoadData.Options; + sld.ReplaceScenes = _globalSceneLoadData.ReplaceScenes; + + LoadQueueData qd = new LoadQueueData(SceneScopeType.Global, Array.Empty(), sld, _globalScenes, false); + //Send message to load the networked scenes. + LoadScenesBroadcast msg = new LoadScenesBroadcast() + { + QueueData = qd + }; + + connection.Broadcast(msg, true); + } + } + + /// + /// Received on client when the server has no start scenes. + /// + private void OnClientEmptyStartScenes(EmptyStartScenesBroadcast msg) + { + TryInvokeLoadedStartScenes(_clientManager.Connection, false); + _clientManager.Broadcast(msg); + } + /// + /// Received on server when client confirms there are no start scenes. + /// + private void OnServerEmptyStartScenes(NetworkConnection conn, EmptyStartScenesBroadcast msg) + { + //Already received, shouldn't be happening again. + if (conn.LoadedStartScenes(true)) + conn.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"Received multiple EmptyStartSceneBroadcast from connectionId {conn.ClientId}. Connection will be kicked immediately."); + else + OnClientLoadedScenes(conn, new ClientScenesLoadedBroadcast()); + } + #endregion + + #region Player disconnect. + /// + /// Received when a player disconnects from the server. + /// + /// //finish. + private void ClientDisconnected(NetworkConnection conn) + { + _pendingClientSceneChanges.Remove(conn); + /* Remove connection from all scenes. While doing so check + * if scene should be unloaded provided there are no more clients + * in the scene, and it's set to automatically unload. This situation is a bit + * unique since a client disconnect happens outside the manager, so there + * isn't much code we can re-use to perform this operation. */ + List scenesToUnload = new List(); + //Current active scene. + Scene activeScene = UnitySceneManager.GetActiveScene(); + foreach (KeyValuePair> item in SceneConnections) + { + Scene scene = item.Key; + HashSet hs = item.Value; + + bool removed = hs.Remove(conn); + /* If no more observers for scene, not a global scene, and not to be manually unloaded + * then remove scene from SceneConnections and unload it. */ + if (removed && hs.Count == 0 && + !IsGlobalScene(scene) && !_manualUnloadScenes.Contains(scene) && + (scene != activeScene)) + scenesToUnload.Add(scene); + } + + //If scenes should be unloaded. + if (scenesToUnload.Count > 0) + { + foreach (Scene s in scenesToUnload) + SceneConnections.Remove(s); + SceneUnloadData sud = new SceneUnloadData(SceneLookupData.CreateData(scenesToUnload)); + UnloadConnectionScenes(Array.Empty(), sud); + } + } + #endregion + + #region Server received messages. + /// + /// Received on server when a client loads scenes. + /// + /// + /// + private void OnClientLoadedScenes(NetworkConnection conn, ClientScenesLoadedBroadcast msg) + { + int pendingLoads; + _pendingClientSceneChanges.TryGetValueIL2CPP(conn, out pendingLoads); + + //There's no loads or unloads pending, kick client. + if (pendingLoads == 0) + { + conn.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"Received excessive ClientScenesLoadedBroadcast from connectionId {conn.ClientId}. Connection will be kicked immediately."); + return; + } + //If there is a load pending then update pending count. + else + { + pendingLoads--; + if (pendingLoads == 0) + _pendingClientSceneChanges.Remove(conn); + else + _pendingClientSceneChanges[conn] = pendingLoads; + } + + if (!Comparers.IsDefault(msg)) + { + foreach (SceneLookupData item in msg.SceneLookupDatas) + { + Scene s = item.GetScene(out _); + if (s.IsValid()) + AddConnectionToScene(conn, s); + } + } + + TryInvokeLoadedStartScenes(conn, true); + } + #endregion + + #region Events. + /// + /// Checks if OnQueueStart should invoke, and if so invokes. + /// + private void TryInvokeOnQueueStart() + { + if (_sceneQueueStartInvoked) + return; + + _sceneQueueStartInvoked = true; + OnQueueStart?.Invoke(); + } + /// + /// Checks if OnQueueEnd should invoke, and if so invokes. + /// + private void TryInvokeOnQueueEnd() + { + if (!_sceneQueueStartInvoked) + return; + + _sceneQueueStartInvoked = false; + OnQueueEnd?.Invoke(); + } + /// + /// Invokes that a scene load has started. Only called when valid scenes will be loaded. + /// + /// + private void InvokeOnSceneLoadStart(LoadQueueData qd) + { + TryInvokeOnQueueStart(); + OnLoadStart?.Invoke(new SceneLoadStartEventArgs(qd)); + } + /// + /// Invokes that a scene load has ended. Only called after a valid scene has loaded. + /// + /// + private void InvokeOnSceneLoadEnd(LoadQueueData qd, List requestedLoadScenes, List loadedScenes, string[] unloadedSceneNames) + { + //Make new list to not destroy original data. + List skippedScenes = requestedLoadScenes.ToList(); + //Remove loaded scenes from requested scenes. + for (int i = 0; i < loadedScenes.Count; i++) + skippedScenes.Remove(loadedScenes[i].name); + + SceneLoadEndEventArgs args = new SceneLoadEndEventArgs(qd, skippedScenes.ToArray(), loadedScenes.ToArray(), unloadedSceneNames); + OnLoadEnd?.Invoke(args); + } + /// + /// Invokes that a scene unload has started. Only called when valid scenes will be unloaded. + /// + /// + private void InvokeOnSceneUnloadStart(UnloadQueueData sqd) + { + TryInvokeOnQueueStart(); + OnUnloadStart?.Invoke(new SceneUnloadStartEventArgs(sqd)); + } + /// + /// Invokes that a scene unload has ended. Only called after a valid scene has unloaded. + /// + /// + private void InvokeOnSceneUnloadEnd(UnloadQueueData sqd, List unloadedScenes, List newUnloadedScenes) + { + SceneUnloadEndEventArgs args = new SceneUnloadEndEventArgs(sqd, unloadedScenes, newUnloadedScenes); + OnUnloadEnd?.Invoke(args); + } + /// + /// Invokes when completion percentage changes while unloading or unloading a scene. Value is between 0f and 1f, while 1f is 100% done. + /// + /// + private void InvokeOnScenePercentChange(LoadQueueData qd, float value) + { + value = Mathf.Clamp(value, 0f, 1f); + SceneLoadPercentEventArgs slp = new SceneLoadPercentEventArgs(qd, value); + OnLoadPercentChange?.Invoke(slp); + } + #endregion + + #region Scene queue processing. + /// + /// Queues a load or unload operation and starts queue if needed. + /// + /// + private void QueueOperation(object data) + { + //Add to scene queue data. + _queuedOperations.Add(data); + /* If only one entry then scene operations are not currently in progress. + * Should there be more than one entry then scene operations are already + * occuring. The coroutine will automatically load in order. */ + + if (_queuedOperations.Count == 1) + StartCoroutine(__ProcessSceneQueue()); + } + /// + /// Processes queued scene operations. + /// + /// + /// + private IEnumerator __ProcessSceneQueue() + { + /* Queue start won't invoke unless a scene load or unload actually occurs. + * For example: if a scene is already loaded, and nothing needs to be loaded, + * queue start will not invoke. */ + + while (_queuedOperations.Count > 0) + { + //If a load scene. + if (_queuedOperations[0] is LoadQueueData) + yield return StartCoroutine(__LoadScenes()); + //If an unload scene. + else if (_queuedOperations[0] is UnloadQueueData) + yield return StartCoroutine(__UnloadScenes()); + + if (_queuedOperations.Count > 0) + _queuedOperations.RemoveAt(0); + } + + TryInvokeOnQueueEnd(); + } + #endregion + + #region LoadScenes + /// + /// Loads scenes on the server and for all clients. Future clients will automatically load these scenes. + /// + /// Data about which scenes to load. + public void LoadGlobalScenes(SceneLoadData sceneLoadData) + { + LoadGlobalScenes_Internal(sceneLoadData, _globalScenes, true); + } + /// + /// Adds to load scene queue. + /// + /// + /// + private void LoadGlobalScenes_Internal(SceneLoadData sceneLoadData, string[] globalScenes, bool asServer) + { + if (!CanExecute(asServer, true)) + return; + if (SceneDataInvalid(sceneLoadData, true)) + return; + if (sceneLoadData.Options.AllowStacking) + { + _networkManager.LogError($"Stacking scenes is not allowed with Global scenes."); + return; + } + + LoadQueueData lqd = new LoadQueueData(SceneScopeType.Global, Array.Empty(), sceneLoadData, globalScenes, asServer); + QueueOperation(lqd); + } + + /// + /// Loads scenes on server and tells connections to load them as well. Other connections will not load this scene. + /// + /// Connections to load scenes for. + /// Data about which scenes to load. + public void LoadConnectionScenes(NetworkConnection conn, SceneLoadData sceneLoadData) + { + LoadConnectionScenes(new NetworkConnection[] { conn }, sceneLoadData); + } + /// + /// Loads scenes on server and tells connections to load them as well. Other connections will not load this scene. + /// + /// Connections to load scenes for. + /// Data about which scenes to load. + public void LoadConnectionScenes(NetworkConnection[] conns, SceneLoadData sceneLoadData) + { + LoadConnectionScenes_Internal(conns, sceneLoadData, _globalScenes, true); + } + /// + /// Loads scenes on server without telling clients to load the scenes. + /// + /// Data about which scenes to load. + public void LoadConnectionScenes(SceneLoadData sceneLoadData) + { + LoadConnectionScenes_Internal(Array.Empty(), sceneLoadData, _globalScenes, true); + } + + /// + /// Adds to load scene queue. + /// + /// + /// + private void LoadConnectionScenes_Internal(NetworkConnection[] conns, SceneLoadData sceneLoadData, string[] globalScenes, bool asServer) + { + if (!CanExecute(asServer, true)) + return; + if (SceneDataInvalid(sceneLoadData, true)) + return; + + LoadQueueData lqd = new LoadQueueData(SceneScopeType.Connections, conns, sceneLoadData, globalScenes, asServer); + QueueOperation(lqd); + } + + /// + /// Returns if a NetworkObject can be moved. + /// + /// + /// + private bool CanMoveNetworkObject(NetworkObject nob, bool warn) + { + //Null. + if (nob == null) + return WarnAndReturnFalse($"NetworkObject is null."); + //Not networked. + if (!nob.IsNetworked) + return WarnAndReturnFalse($"NetworkObject {nob.name} cannot be moved as it is not networked."); + //Not spawned. + if (!nob.IsSpawned) + return WarnAndReturnFalse($"NetworkObject {nob.name} canot be moved as it is not spawned."); + //SceneObject. + if (nob.IsSceneObject) + return WarnAndReturnFalse($"NetworkObject {nob.name} cannot be moved as it is a scene object."); + //Not root. + if (nob.transform.parent != null) + return WarnAndReturnFalse($"NetworkObject {nob.name} cannot be moved because it is not the root object. Unity can only move root objects between scenes."); + //In DDOL and IsGlobal. + if (nob.IsGlobal && (nob.gameObject.scene.name == DDOLFinder.GetDDOL().gameObject.scene.name)) + return WarnAndReturnFalse("NetworkObject {nob.name} cannot be moved because it is global. Global objects must remain in the DontDestroyOnLoad scene."); + + //Fall through success. + return true; + + bool WarnAndReturnFalse(string msg) + { + if (warn) + _networkManager.LogWarning(msg); + return false; + } + } + + /// + /// Loads a connection scene queue data. This behaves just like a networked scene load except it sends only to the specified connections, and it always loads as an additive scene on server. + /// + /// + private IEnumerator __LoadScenes() + { + LoadQueueData data = _queuedOperations[0] as LoadQueueData; + SceneLoadData sceneLoadData = data.SceneLoadData; + //True if running as server. + bool asServer = data.AsServer; + //True if running as client, while network server is active. + bool asHost = (!asServer && _networkManager.IsServer); + + //If connection went inactive. + if (!ConnectionActive(asServer)) + yield break; + + /* Scene sanity checks. */ + if (sceneLoadData.SceneLookupDatas.Length == 0) + { + _networkManager.LogWarning($"No scenes specified to load."); + yield break; + } + + //True if replacing scenes with specified ones. + ReplaceOption replaceScenes = sceneLoadData.ReplaceScenes; + + //May be unset if on server, this is fine. + NetworkConnection localConnection = _networkManager.ClientManager.Connection; + /* Immediately set new global scenes. If on client this is whatever + * server passes in. This should be set even if scope type + * is not global because clients might get a connection scene first. + */ + if (!asServer) + { + if (!asHost) + _globalScenes = data.GlobalScenes; + } + /* However, if server, then only update global scenes if scope + * is global. */ + else if (asServer && data.ScopeType == SceneScopeType.Global) + { + _globalSceneLoadData = sceneLoadData; + string[] names = sceneLoadData.SceneLookupDatas.GetNames(); + //If replacing. + if (replaceScenes != ReplaceOption.None) + { + _globalScenes = names; + } + //Add onto. + else + { + int index = _globalScenes.Length; + Array.Resize(ref _globalScenes, _globalScenes.Length + names.Length); + Array.Copy(names, 0, _globalScenes, index, names.Length); + } + + data.GlobalScenes = _globalScenes; + } + + + /* Scene queue data scenes. + * All scenes in the scene queue data whether they will be loaded or not. */ + List requestedLoadSceneNames = new List(); + List requestedLoadSceneHandles = new List(); + + /* Make a null filled array. This will be populated + * using loaded scenes, or already loaded (eg cannot be loaded) scenes. */ + SceneLookupData[] broadcastLookupDatas = new SceneLookupData[sceneLoadData.SceneLookupDatas.Length]; + + /* LoadableScenes and SceneReferenceDatas. + /* Will contain scenes which may be loaded. + * Scenes might not be added to loadableScenes + * if for example loadOnlyUnloaded is true and + * the scene is already loaded. */ + List loadableScenes = new List(); + for (int i = 0; i < sceneLoadData.SceneLookupDatas.Length; i++) + { + SceneLookupData lookupData = sceneLoadData.SceneLookupDatas[i]; + //Scene to load. + bool byHandle; + Scene s = lookupData.GetScene(out byHandle); + //If found then add it to requestedLoadScenes. + if (s.IsValid()) + { + requestedLoadSceneNames.Add(s.name); + if (byHandle) + requestedLoadSceneHandles.Add(s.handle); + } + + if (CanLoadScene(data, lookupData)) + { + //Don't load if as host, server side would have loaded already. + if (!asHost) + loadableScenes.Add(lookupData); + } + //Only the server needs to find scene handles to send to client. Client will send these back to the server. + else if (asServer) + { + /* If here then scene cannot be loaded, which + * can only happen if the scene already exists. + * Find the scene using sld and set to datas. */ + /* Set at the index of i. This way should the current + * SLD not be the first scene it won't fill the + * first slot in broadcastLookupDatas. This is important + * because the first slot is used for the single scene + * when using replace scenes. */ + broadcastLookupDatas[i] = new SceneLookupData(s); + } + } + + /* Move identities + * to holder scene to preserve them. + * Required if a single scene is specified. Cannot rely on + * loadSingleScene since it is only true if the single scene + * must be loaded, which may be false if it's already loaded on + * the server. */ + //Do not run if running as client, and server is active. This would have already run as server. + if (!asHost) + { + foreach (NetworkObject nob in sceneLoadData.MovedNetworkObjects) + { + //NetworkObject might be null if client lost observation of it. + if (nob != null && CanMoveNetworkObject(nob, true)) + UnitySceneManager.MoveGameObjectToScene(nob.gameObject, GetMovedObjectsScene()); + } + } + + //Connection scenes handles prior to ConnectionScenes being modified. + List connectionScenesHandlesCached = new List(); + //If replacing scenes. + if (replaceScenes != ReplaceOption.None) + { + /* Resetting SceneConnections. */ + /* If server and replacing scenes. + * It's important to run this AFTER moving MovedNetworkObjects + * so that they are no longer in the scenes they are leaving. Otherwise + * the scene condition would pick them up as still in the leaving scene. */ + if (asServer) + { + Scene[] sceneConnectionsKeys = SceneConnections.Keys.ToArray(); + for (int i = 0; i < sceneConnectionsKeys.Length; i++) + connectionScenesHandlesCached.Add(sceneConnectionsKeys[i].handle); + + //If global then remove all connections from all scenes. + if (data.ScopeType == SceneScopeType.Global) + { + foreach (Scene s in sceneConnectionsKeys) + RemoveAllConnectionsFromScene(s); + } + //Connections. + else if (data.ScopeType == SceneScopeType.Connections) + { + RemoveConnectionsFromNonGlobalScenes(data.Connections); + } + } + //As client set scenes id cache to local connection scenes. + else + { + foreach (Scene s in _networkManager.ClientManager.Connection.Scenes) + connectionScenesHandlesCached.Add(s.handle); + } + } + + + /* Scene unloading if replacing scenes. + * + * Unload all scenes except MovedObjectsHolder. Also don't + * unload GlobalScenes if loading as connection. */ + List unloadableScenes = new List(); + //Do not run if running as client, and server is active. This would have already run as server. + if ((replaceScenes != ReplaceOption.None) && !asHost) + { + //See what scenes can be unloaded based on replace options. + for (int i = 0; i < UnitySceneManager.sceneCount; i++) + { + Scene s = UnitySceneManager.GetSceneAt(i); + //MovedObjectsScene will never be unloaded. + if (s == GetMovedObjectsScene()) + continue; + /* Scene is in one of the scenes being loaded. + * This can occur when trying to load additional clients + * into an existing scene. */ + if (requestedLoadSceneNames.Contains(s.name)) + continue; + //Same as above but using handles. + if (requestedLoadSceneHandles.Contains(s.handle)) + continue; + /* Cannot unload global scenes. If + * replace scenes was used for a global + * load then global scenes would have been reset + * before this. */ + if (IsGlobalScene(s)) + continue; + //If scene must be manually unloaded then it cannot be unloaded here. + if (_manualUnloadScenes.Contains(s)) + continue; + + bool inScenesCache = connectionScenesHandlesCached.Contains(s.handle); + HashSet conns; + bool inScenesCurrent = SceneConnections.ContainsKey(s); + //If was in scenes previously but isnt now then no connections reside in the scene. + if (inScenesCache && !inScenesCurrent) + { + //Intentionally left blank. + } + //If still in cache see if any connections exist. + else if (SceneConnections.TryGetValueIL2CPP(s, out conns)) + { + //Still has clients in scene. + if (conns != null && conns.Count > 0) + continue; + } + //An offline scene. + else + { + //If not replacing all scenes then skip offline scenes. + if (replaceScenes != ReplaceOption.All) + continue; + } + + unloadableScenes.Add(s); + } + } + + /* Start event. */ + if (unloadableScenes.Count > 0 || loadableScenes.Count > 0) + { + InvokeOnSceneLoadStart(data); + _sceneProcessor.LoadStart(data); + } + //Unloaded scenes by name. Only used for information within callbacks. + string[] unloadedNames = new string[unloadableScenes.Count]; + for (int i = 0; i < unloadableScenes.Count; i++) + unloadedNames[i] = unloadableScenes[i].name; + /* Before unloading if !asServer and !asHost and replacing scenes + * then move all non scene networked objects to the moved + * objects holder. Otherwise network objects would get destroyed + * on the scene change and never respawned if server doesn't + * have a reason to update visibility. */ + if (!data.AsServer && !asHost && (replaceScenes != ReplaceOption.None)) + { + Scene s = GetMovedObjectsScene(); + foreach (NetworkObject nob in _networkManager.ClientManager.Objects.Spawned.Values) + { + if (CanMoveNetworkObject(nob, false)) + UnitySceneManager.MoveGameObjectToScene(nob.gameObject, s); + } + } + /* Unloading scenes. */ + _sceneProcessor.UnloadStart(data); + for (int i = 0; i < unloadableScenes.Count; i++) + { + MoveClientHostObjects(unloadableScenes[i], asServer); + //Unload one at a time. + _sceneProcessor.BeginUnloadAsync(unloadableScenes[i]); + while (!_sceneProcessor.IsPercentComplete()) + yield return null; + } + _sceneProcessor.UnloadEnd(data); + + //Scenes loaded. + List loadedScenes = new List(); + /* Scene loading. + /* Use additive to not thread lock server. */ + for (int i = 0; i < loadableScenes.Count; i++) + { + //Start load async and wait for it to finish. + LoadSceneParameters loadSceneParameters = new LoadSceneParameters() + { + loadSceneMode = LoadSceneMode.Additive, + localPhysicsMode = sceneLoadData.Options.LocalPhysics + }; + + /* How much percentage each scene load can be worth + * at maximum completion. EG: if there are two scenes + * 1f / 2f is 0.5f. */ + float maximumIndexWorth = (1f / (float)loadableScenes.Count); + + _sceneProcessor.BeginLoadAsync(loadableScenes[i].Name, loadSceneParameters); + while (!_sceneProcessor.IsPercentComplete()) + { + float percent = _sceneProcessor.GetPercentComplete(); + InvokePercentageChange(i, maximumIndexWorth, percent); + yield return null; + } + + //Invokes OnScenePercentChange with progress. + void InvokePercentageChange(int index, float maximumWorth, float currentScenePercent) + { + /* Total percent will be how much percentage is complete + * in total. Initialize it with a value based on how many + * scenes are already fully loaded. */ + float totalPercent = (index * maximumWorth); + //Add this scenes progress onto total percent. + totalPercent += Mathf.Lerp(0f, maximumWorth, currentScenePercent); + //Dispatch with total percent. + InvokeOnScenePercentChange(data, totalPercent); + } + + //Add to loaded scenes. + Scene loaded = UnitySceneManager.GetSceneAt(UnitySceneManager.sceneCount - 1); + loadedScenes.Add(loaded); + _sceneProcessor.AddLoadedScene(loaded); + } + //When all scenes are loaded invoke with 100% done. + InvokeOnScenePercentChange(data, 1f); + + /* Add to ManuallyUnloadScenes. */ + if (data.AsServer && !sceneLoadData.Options.AutomaticallyUnload) + { + foreach (Scene s in loadedScenes) + _manualUnloadScenes.Add(s); + } + /* Move identities to first scene. */ + if (!asHost) + { + //Find the first valid scene to move objects to. + Scene firstValidScene = default; + //If to stack scenes. + if (sceneLoadData.Options.AllowStacking) + { + Scene firstScene = sceneLoadData.GetFirstLookupScene(); + /* If the first lookup data contains a handle and the scene + * is found for that handle then use that as the moved to scene. + * Nobs always move to the first specified scene. */ + if (sceneLoadData.SceneLookupDatas[0].Handle != 0 && !string.IsNullOrEmpty(firstScene.name)) + { + firstValidScene = firstScene; + } + //If handle is not specified then used the last scene that has the same name as the first lookupData. + else + { + Scene lastSameSceneName = default; + for (int i = 0; i < UnitySceneManager.sceneCount; i++) + { + Scene s = UnitySceneManager.GetSceneAt(i); + if (s.name == firstScene.name) + lastSameSceneName = s; + } + + /* Shouldn't be possible since the scene will always exist either by + * just being loaded or already loaded. */ + if (string.IsNullOrEmpty(lastSameSceneName.name)) + _networkManager.LogError($"Scene {sceneLoadData.SceneLookupDatas[0].Name} could not be found in loaded scenes."); + else + firstValidScene = lastSameSceneName; + } + } + //Not stacking. + else + { + firstValidScene = sceneLoadData.GetFirstLookupScene(); + //If not found by look then try firstloaded. + if (string.IsNullOrEmpty(firstValidScene.name)) + firstValidScene = GetFirstLoadedScene(); + } + + //Gets first scene loaded this method call. + Scene GetFirstLoadedScene() + { + if (loadedScenes.Count > 0) + return loadedScenes[0]; + else + return default; + } + + //If firstValidScene is still invalid then throw. + if (string.IsNullOrEmpty(firstValidScene.name)) + { + _networkManager.LogError($"Unable to move objects to a new scene because new scene lookup has failed."); + } + //Move objects from movedobejctsscene to first valid scene. + else + { + Scene s = GetMovedObjectsScene(); + s.GetRootGameObjects(_movingObjects); + + foreach (GameObject go in _movingObjects) + UnitySceneManager.MoveGameObjectToScene(go, firstValidScene); + } + } + + _sceneProcessor.ActivateLoadedScenes(); + //Wait until everything is loaded (done). + yield return _sceneProcessor.AsyncsIsDone(); + _sceneProcessor.LoadEnd(data); + + /* Wait until loadedScenes are all marked as done. + * This is an extra precautionary step because on some devices + * the AsyncIsDone returns true before scenes are actually loaded. */ + bool allScenesLoaded = true; + do + { + foreach (Scene s in loadedScenes) + { + if (!s.isLoaded) + { + allScenesLoaded = false; + break; + } + } + yield return null; + } while (!allScenesLoaded); + + SetActiveScene_Local(); + void SetActiveScene_Local() + { + bool byUser; + Scene preferredActiveScene = GetUserPreferredActiveScene(sceneLoadData.PreferredActiveScene, out byUser); + //If preferred still is not set then try to figure it out. + if (!preferredActiveScene.IsValid()) + { + /* Populate preferred scene to first loaded if replacing + * scenes for connection. Does not need to be set for + * global because when a global exist it's always set + * as the active scene. + * + * Do not set preferred scene if server as this could cause + * problems when stacking or connection specific scenes. Let the + * user make those changes. */ + if (sceneLoadData.ReplaceScenes != ReplaceOption.None && data.ScopeType == SceneScopeType.Connections && !_networkManager.IsServer) + preferredActiveScene = sceneLoadData.GetFirstLookupScene(); + } + + SetActiveScene(preferredActiveScene, byUser); + } + + //Only the server needs to find scene handles to send to client. Client will send these back to the server. + if (asServer) + { + //Populate broadcastLookupDatas with any loaded scenes. + foreach (Scene s in loadedScenes) + { + SetInFirstNullIndex(s); + + //Sets scene in the first null index of broadcastLookupDatas. + void SetInFirstNullIndex(Scene scene) + { + for (int i = 0; i < broadcastLookupDatas.Length; i++) + { + if (broadcastLookupDatas[i] == null) + { + broadcastLookupDatas[i] = new SceneLookupData(scene); + return; + } + } + + //If here there are no null entries. + _networkManager.LogError($"Cannot add scene to broadcastLookupDatas, collection is full."); + } + } + } + + /* If running as server and server is + * active then send scene changes to client. + * Making sure server is still active should it maybe + * have dropped during scene loading. */ + if (data.AsServer && _networkManager.IsServer) + { + //Tell clients to load same scenes. + LoadScenesBroadcast msg = new LoadScenesBroadcast() + { + QueueData = data + }; + //Replace scene lookup datas with ones intended to broadcast to client. + msg.QueueData.SceneLoadData.SceneLookupDatas = broadcastLookupDatas; + //If networked scope then send to all. + if (data.ScopeType == SceneScopeType.Global) + { + NetworkConnection[] conns = _serverManager.Clients.Values.ToArray(); + AddPendingLoad(conns); + _serverManager.Broadcast(msg, true); + } + //If connections scope then only send to connections. + else if (data.ScopeType == SceneScopeType.Connections) + { + AddPendingLoad(data.Connections); + for (int i = 0; i < data.Connections.Length; i++) + { + if (data.Connections[i].Authenticated) + data.Connections[i].Broadcast(msg, true); + } + } + } + /* If running as client then send a message + * to the server to tell them the scene was loaded. + * This allows the server to add the client + * to the scene for checkers. */ + else if (!data.AsServer && _networkManager.IsClient) + { + ClientScenesLoadedBroadcast msg = new ClientScenesLoadedBroadcast() + { + SceneLookupDatas = sceneLoadData.SceneLookupDatas + }; + _clientManager.Broadcast(msg); + + //Remove from old scenes. + foreach (Scene item in unloadableScenes) + { + if (item.IsValid()) + localConnection.RemoveFromScene(item); + } + //Add local client to scenes. + foreach (Scene item in loadedScenes) + localConnection.AddToScene(item); + } + + InvokeOnSceneLoadEnd(data, requestedLoadSceneNames, loadedScenes, unloadedNames); + } + + /// + /// Received on client when connection scenes must be loaded. + /// + /// + /// + private void OnLoadScenes(LoadScenesBroadcast msg) + { + //Null data is sent by the server when there are no start scenes to load. + if (msg.QueueData == null) + { + TryInvokeLoadedStartScenes(_clientManager.Connection, false); + return; + } + + LoadQueueData qd = msg.QueueData; + if (qd.ScopeType == SceneScopeType.Global) + LoadGlobalScenes_Internal(qd.SceneLoadData, qd.GlobalScenes, false); + else + LoadConnectionScenes_Internal(Array.Empty(), qd.SceneLoadData, qd.GlobalScenes, false); + } + #endregion + + #region UnloadScenes. + /// + /// Unloads scenes on the server and for all clients. + /// + /// Data about which scenes to unload. + public void UnloadGlobalScenes(SceneUnloadData sceneUnloadData) + { + if (!CanExecute(true, true)) + return; + + UnloadGlobalScenes_Internal(sceneUnloadData, _globalScenes, true); + } + /// + /// + /// + /// + /// + /// + /// + private void UnloadGlobalScenes_Internal(SceneUnloadData sceneUnloadData, string[] globalScenes, bool asServer) + { + UnloadQueueData uqd = new UnloadQueueData(SceneScopeType.Global, Array.Empty(), sceneUnloadData, globalScenes, asServer); + QueueOperation(uqd); + } + + + /// + /// Unloads scenes on server and tells a connection to unload them as well. Other connections will not unload this scene. + /// + /// Connection to unload scenes for. + /// Data about which scenes to unload. + public void UnloadConnectionScenes(NetworkConnection connection, SceneUnloadData sceneUnloadData) + { + UnloadConnectionScenes(new NetworkConnection[] { connection }, sceneUnloadData); + } + /// + /// Unloads scenes on server and tells connections to unload them as well. Other connections will not unload this scene. + /// + /// Connections to unload scenes for. + /// Data about which scenes to unload. + public void UnloadConnectionScenes(NetworkConnection[] connections, SceneUnloadData sceneUnloadData) + { + UnloadConnectionScenes_Internal(connections, sceneUnloadData, _globalScenes, true); + } + + /// + /// Unloads scenes on server without telling any connections to unload them. + /// + /// Data about which scenes to unload. + public void UnloadConnectionScenes(SceneUnloadData sceneUnloadData) + { + UnloadConnectionScenes_Internal(Array.Empty(), sceneUnloadData, _globalScenes, true); + } + /// + /// Unloads scenes for connections. + /// + /// + /// + /// + /// + private void UnloadConnectionScenes_Internal(NetworkConnection[] connections, SceneUnloadData sceneUnloadData, string[] globalScenes, bool asServer) + { + if (!CanExecute(asServer, true)) + return; + if (SceneDataInvalid(sceneUnloadData, true)) + return; + + UnloadQueueData uqd = new UnloadQueueData(SceneScopeType.Connections, connections, sceneUnloadData, globalScenes, asServer); + QueueOperation(uqd); + } + /// + /// Loads scenes within QueuedSceneLoads. + /// + /// + private IEnumerator __UnloadScenes() + { + UnloadQueueData data = _queuedOperations[0] as UnloadQueueData; + SceneUnloadData sceneUnloadData = data.SceneUnloadData; + + //If connection went inactive. + if (!ConnectionActive(data.AsServer)) + yield break; + + /* Some actions should not run as client if server is also active. + * This is to keep things from running twice. */ + bool asClientHost = (!data.AsServer && _networkManager.IsServer); + ///True if running asServer. + bool asServer = data.AsServer; + + //Get scenes to unload. + Scene[] scenes = GetScenes(sceneUnloadData.SceneLookupDatas); + /* No scenes found. Only run this if not asHost. + * While asHost scenes will possibly not exist because + * server side has already unloaded them. But rest of + * the unload should continue. */ + if (scenes.Length == 0 && !asClientHost) + { + _networkManager.LogWarning($"No scenes were found to unload."); + yield break; + } + + /* Remove from global scenes + * if server and scope is global. + * All passed in scenes should be removed from global + * regardless of if they're valid or not. If they are invalid, + * then they shouldn't be in global to begin with. */ + if (asServer && data.ScopeType == SceneScopeType.Global) + { + RemoveFromGlobalScenes(sceneUnloadData.SceneLookupDatas); + //Update queue data. + data.GlobalScenes = _globalScenes; + } + + /* Remove connections. */ + if (asServer) + { + foreach (Scene s in scenes) + { + //If global then remove all connections. + if (data.ScopeType == SceneScopeType.Global) + RemoveAllConnectionsFromScene(s); + //Connections. + else if (data.ScopeType == SceneScopeType.Connections) + RemoveConnectionsFromScene(data.Connections, s); + } + } + + + /* This will contain all scenes which can be unloaded. + * The collection will be modified through various checks. */ + List unloadableScenes = scenes.ToList(); + /* Unloaded scenes manually created to overcome + * the empty names in Scene structs after Unity unloads + * a scene. */ + List unloadedScenes = new List(); + /* If asServer and KeepUnused then clear all unloadables. + * The clients will still unload the scenes. */ + if ((asServer || asClientHost) && sceneUnloadData.Options.Mode == UnloadOptions.ServerUnloadMode.KeepUnused) + unloadableScenes.Clear(); + /* Check to remove global scenes unloadableScenes. + * This will need to be done if scenes are being unloaded + * for connections. Global scenes cannot be unloaded as + * connection. */ + if (data.ScopeType == SceneScopeType.Connections) + RemoveGlobalScenes(unloadableScenes); + //If set to unload unused only. + if (sceneUnloadData.Options.Mode == UnloadOptions.ServerUnloadMode.UnloadUnused) + RemoveOccupiedScenes(unloadableScenes); + + //If there are scenes to unload. + if (unloadableScenes.Count > 0) + { + InvokeOnSceneUnloadStart(data); + _sceneProcessor.UnloadStart(data); + + //Begin unloading. + foreach (Scene s in unloadableScenes) + { + unloadedScenes.Add(new UnloadedScene(s)); + MoveClientHostObjects(s, asServer); + /* Remove from manualUnloadedScenes. + * Scene may not be in this collection + * but removing is one call vs checking + * then removing. */ + _manualUnloadScenes.Remove(s); + + _sceneProcessor.BeginUnloadAsync(s); + while (!_sceneProcessor.IsPercentComplete()) + yield return null; + } + + _sceneProcessor.UnloadEnd(data); + } + + /* Must yield after sceneProcessor handles things. + * This is a Unity bug of sorts. I'm not entirely sure what + * is happening, but without the yield it seems as though + * the processor logic doesn't complete. This doesn't make much + * sense given unity is supposed to be single threaded. Must be + * something to do with the coroutine. */ + yield return null; + + bool byUser; + Scene preferredActiveScene = GetUserPreferredActiveScene(sceneUnloadData.PreferredActiveScene, out byUser); + SetActiveScene(preferredActiveScene, byUser); + + /* If running as server then make sure server + * is still active after the unloads. If so + * send out unloads to clients. */ + if (asServer && ConnectionActive(true)) + { + //Tell clients to unload same scenes. + UnloadScenesBroadcast msg = new UnloadScenesBroadcast() + { + QueueData = data + }; + //Global. + if (data.ScopeType == SceneScopeType.Global) + { + _serverManager.Broadcast(msg, true); + } + //Connections. + else if (data.ScopeType == SceneScopeType.Connections) + { + if (data.Connections != null) + { + for (int i = 0; i < data.Connections.Length; i++) + { + if (data.Connections[i] != null) + data.Connections[i].Broadcast(msg, true); + } + } + } + } + else if (!asServer) + { + NetworkConnection localConnection = _networkManager.ClientManager.Connection; + //Remove from old scenes. + foreach (Scene item in unloadableScenes) + { + if (item.IsValid()) + localConnection.RemoveFromScene(item); + } + } + + InvokeOnSceneUnloadEnd(data, unloadableScenes, unloadedScenes); + } + + + /// + /// Received on clients when networked scenes must be unloaded. + /// + /// + /// + private void OnUnloadScenes(UnloadScenesBroadcast msg) + { + UnloadQueueData qd = msg.QueueData; + if (qd.ScopeType == SceneScopeType.Global) + UnloadGlobalScenes_Internal(qd.SceneUnloadData, qd.GlobalScenes, false); + else + UnloadConnectionScenes_Internal(Array.Empty(), qd.SceneUnloadData, qd.GlobalScenes, false); + } + #endregion + + /// + /// Move objects visible to clientHost that are within an unloading scene.This ensures the objects are despawned on the client side rather than when the scene is destroyed. + /// + /// + private void MoveClientHostObjects(Scene scene, bool asServer) + { + if (!_moveClientHostObjects) + return; + /* The asServer isn't really needed. I could only call + * this method when asServer is true. But for the sake + * of preventing user-error (me being the user this time) + * I've included it into the parameters. */ + if (!asServer) + return; + //Don't need to perform if not host. + if (!_networkManager.IsClient) + return; + + NetworkConnection clientConn = _networkManager.ClientManager.Connection; + /* It would be nice to see if the client wasn't even in the scene + * here using SceneConnections but it's possible that the scene had been + * wiped from SceneConnections earlier depending on how scenes are + * loaded or unloaded. Instead we must iterate through spawned objects. */ + + ListCache movingNobs = ListCaches.GetNetworkObjectCache(); + /* Rather than a get all networkobjects in scene + * let's iterate the spawned objects instead. I imagine + * in most scenarios iterating spawned would be faster. + * That's a long one! */ + foreach (NetworkObject nob in _networkManager.ServerManager.Objects.Spawned.Values) + { + //Not in the scene being destroyed. + if (nob.gameObject.scene != scene) + continue; + //ClientHost doesn't have visibility. + if (!nob.Observers.Contains(clientConn)) + continue; + //Cannot move if not root. + if (nob.transform.root != null) + continue; + + /* If here nob is in the same being + * destroyed and clientHost has visiblity. */ + movingNobs.AddValue(nob); + } + + int count = movingNobs.Written; + if (count > 0) + { + Scene moveScene = GetDelayedDestroyScene(); + List collection = movingNobs.Collection; + + for (int i = 0; i < count; i++) + { + NetworkObject nob = collection[i]; + /* Force as not a scene object + * so that it becomes destroyed + * rather than disabled. */ + nob.ClearRuntimeSceneObject(); + /* If the object is already being despawned then + *just disable and move it. Otherwise despawn it + * on the server then move it. */ + //Not deinitializing, despawn it then. + if (!nob.IsDeinitializing) + nob.Despawn(); + else + nob.gameObject.SetActive(false); + + UnitySceneManager.MoveGameObjectToScene(nob.gameObject, moveScene); + } + } + ListCaches.StoreCache(movingNobs); + } + + /// + /// Returns if a connection is in a scene using SceneConnections. + /// + /// + /// + /// + internal bool InSceneConnections(NetworkConnection conn, Scene scene) + { + if (!SceneConnections.TryGetValueIL2CPP(scene, out HashSet hs)) + return false; + else + return hs.Contains(conn); + } + + /// + /// Adds the owner of nob to the gameObjects scene if there are no global scenes. + /// + public void AddOwnerToDefaultScene(NetworkObject nob) + { + //No owner. + if (!nob.Owner.IsValid) + { + _networkManager.LogWarning($"NetworkObject {nob.name} does not have an owner."); + return; + } + //Won't add to default if there are globals. + if (_globalScenes != null && _globalScenes.Length > 0) + return; + + AddConnectionToScene(nob.Owner, nob.gameObject.scene); + } + + /// + /// Adds a connection to a scene. This will always be called one connection at a time because connections are only added after they invidually validate loading the scene. + /// Exposed for power users, use caution. + /// + /// Connection to add. + /// Scene to add the connection to. + public void AddConnectionToScene(NetworkConnection conn, Scene scene) + { + HashSet hs; + //Scene doesn't have any connections yet. + bool inSceneConnections = SceneConnections.TryGetValueIL2CPP(scene, out hs); + if (!inSceneConnections) + hs = new HashSet(); + + bool added = hs.Add(conn); + if (added) + { + conn.AddToScene(scene); + + //If not yet added to scene connections. + if (!inSceneConnections) + SceneConnections[scene] = hs; + + NetworkConnection[] arrayConn = new NetworkConnection[] { conn }; + InvokeClientPresenceChange(scene, arrayConn, true, true); + RebuildObservers(arrayConn.ToArray()); + InvokeClientPresenceChange(scene, arrayConn, true, false); + + /* Also need to rebuild all networkobjects + * for connection so other players can + * see them. */ + RebuildObservers(conn.Objects.ToArray()); + } + } + + + /// + /// Removes connections from any scene which is not global. + /// Exposed for power users, use caution. + /// + /// + public void RemoveConnectionsFromNonGlobalScenes(NetworkConnection[] conns) + { + List removedScenes = new List(); + + foreach (KeyValuePair> item in SceneConnections) + { + Scene scene = item.Key; + //Cannot remove from globla scenes. + if (IsGlobalScene(scene)) + continue; + + HashSet hs = item.Value; + List connectionsRemoved = new List(); + //Remove every connection from the scene. + foreach (NetworkConnection c in conns) + { + bool removed = hs.Remove(c); + if (removed) + { + c.RemoveFromScene(scene); + connectionsRemoved.Add(c); + } + } + + //If hashset is empty then remove scene from SceneConnections. + if (hs.Count == 0) + removedScenes.Add(scene); + + if (connectionsRemoved.Count > 0) + { + NetworkConnection[] connectionsRemovedArray = connectionsRemoved.ToArray(); + InvokeClientPresenceChange(scene, connectionsRemovedArray, false, true); + RebuildObservers(connectionsRemovedArray); + InvokeClientPresenceChange(scene, connectionsRemovedArray, false, false); + } + } + + foreach (Scene s in removedScenes) + SceneConnections.Remove(s); + + /* Also rebuild observers for objects owned by connection. + * This ensures other connections will lose visibility if + * they no longer share a scene. */ + foreach (NetworkConnection c in conns) + RebuildObservers(c.Objects.ToArray()); + } + + + /// + /// Removes connections from specified scenes. + /// Exposed for power users, use caution. + /// + /// Connections to remove. + /// Scene to remove from. + public void RemoveConnectionsFromScene(NetworkConnection[] conns, Scene scene) + { + HashSet hs; + //No hashset for scene, so no connections are in scene. + if (!SceneConnections.TryGetValueIL2CPP(scene, out hs)) + return; + + List connectionsRemoved = new List(); + //Remove every connection from the scene. + foreach (NetworkConnection c in conns) + { + bool removed = hs.Remove(c); + if (removed) + { + c.RemoveFromScene(scene); + connectionsRemoved.Add(c); + } + } + + //If hashset is empty then remove scene from SceneConnections. + if (hs.Count == 0) + SceneConnections.Remove(scene); + + if (connectionsRemoved.Count > 0) + { + NetworkConnection[] connectionsRemovedArray = connectionsRemoved.ToArray(); + InvokeClientPresenceChange(scene, connectionsRemovedArray, false, true); + RebuildObservers(connectionsRemovedArray); + InvokeClientPresenceChange(scene, connectionsRemovedArray, false, false); + } + + /* Also rebuild observers for objects owned by connection. + * This ensures other connections will lose visibility if + * they no longer share a scene. */ + foreach (NetworkConnection c in conns) + RebuildObservers(c.Objects.ToArray()); + } + + /// + /// Removes all connections from a scene. + /// + /// Scene to remove connections from. + public void RemoveAllConnectionsFromScene(Scene scene) + { + HashSet hs; + //No hashset for scene, so no connections are in scene. + if (!SceneConnections.TryGetValueIL2CPP(scene, out hs)) + return; + + //On each connection remove them from specified scene. + foreach (NetworkConnection c in hs) + c.RemoveFromScene(scene); + //Make hashset into list for presence change. + NetworkConnection[] connectionsRemoved = hs.ToArray(); + + //Clear hashset and remove entry from sceneconnections. + hs.Clear(); + SceneConnections.Remove(scene); + + if (connectionsRemoved.Length > 0) + { + InvokeClientPresenceChange(scene, connectionsRemoved, false, true); + RebuildObservers(connectionsRemoved); + InvokeClientPresenceChange(scene, connectionsRemoved, false, false); + } + + /* Also rebuild observers for objects owned by connection. + * This ensures other connections will lose visibility if + * they no longer share a scene. */ + foreach (NetworkConnection c in connectionsRemoved) + RebuildObservers(c.Objects.ToArray()); + } + + #region Can Load/Unload Scene. + /// + /// Returns if a scene can be loaded locally. + /// + /// + private bool CanLoadScene(LoadQueueData qd, SceneLookupData sld) + { + bool foundByHandle; + Scene s = sld.GetScene(out foundByHandle); + //Try to find if scene is already loaded. + bool alreadyLoaded = !string.IsNullOrEmpty(s.name); + + if (alreadyLoaded) + { + //Only servers can load the same scene multiple times for stacking. + if (!qd.AsServer) + return false; + //If can only load scenes which aren't loaded yet and scene is already loaded. + if (!qd.SceneLoadData.Options.AllowStacking) + return false; + /* Found by handle, this means the user is trying to specify + * exactly which scene to load into. When a handle is specified + * new instances will not be created, so a new scene cannot + * be loaded. */ + if (alreadyLoaded && foundByHandle) + return false; + } + + //Fall through. + return true; + } + #endregion + + #region Helpers. + /// + /// Rebuilds observers for networkObjects. + /// + /// + private void RebuildObservers(NetworkObject[] networkObjects) + { + foreach (NetworkObject nob in networkObjects) + { + if (nob != null && nob.IsSpawned) + _serverManager.Objects.RebuildObservers(nob); + } + } + /// + /// Rebuilds all NetworkObjects for connection. + /// + internal void RebuildObservers(NetworkConnection connection) + { + RebuildObservers(new NetworkConnection[] { connection }); + } + /// + /// Rebuilds all NetworkObjects for connections. + /// + internal void RebuildObservers(NetworkConnection[] connections) + { + foreach (NetworkConnection c in connections) + _serverManager.Objects.RebuildObservers(c); + } + /// + /// Invokes OnClientPresenceChange start or end. + /// + /// + /// + /// + /// + private void InvokeClientPresenceChange(Scene scene, NetworkConnection[] conns, bool added, bool start) + { + foreach (NetworkConnection c in conns) + { + ClientPresenceChangeEventArgs cpc = new ClientPresenceChangeEventArgs(scene, c, added); + if (start) + OnClientPresenceChangeStart?.Invoke(cpc); + else + OnClientPresenceChangeEnd?.Invoke(cpc); + } + } + #endregion + + #region GetScene. + /// + /// Gets scenes from SceneLookupData. + /// + /// + /// + private Scene[] GetScenes(SceneLookupData[] datas) + { + List result = new List(); + foreach (SceneLookupData sld in datas) + { + Scene s = sld.GetScene(out _); + if (!string.IsNullOrEmpty(s.name)) + result.Add(s); + } + + return result.ToArray(); + } + + /// + /// Returns a scene by name. + /// + /// + /// + public static Scene GetScene(string sceneName) + { + return UnitySceneManager.GetSceneByName(sceneName); + } + /// + /// Returns a scene by handle. + /// + /// + /// + public static Scene GetScene(int sceneHandle) + { + int count = UnitySceneManager.sceneCount; + for (int i = 0; i < count; i++) + { + Scene s = UnitySceneManager.GetSceneAt(i); + if (s.handle == sceneHandle) + return s; + } + + return new Scene(); + } + #endregion + + /// + /// Returns if GlobalScenes contains scene. + /// + /// + /// + private bool IsGlobalScene(Scene scene) + { + for (int i = 0; i < _globalScenes.Length; i++) + { + if (_globalScenes[i] == scene.name) + return true; + } + + return false; + } + + /// + /// Removes datas from GlobalScenes. + /// + /// + private void RemoveFromGlobalScenes(Scene scene) + { + RemoveFromGlobalScenes(new SceneLookupData[] { SceneLookupData.CreateData(scene) }); + } + /// + /// Removes datas from GlobalScenes. + /// + /// + private void RemoveFromGlobalScenes(SceneLookupData[] datas) + { + List newGlobalScenes = _globalScenes.ToList(); + int startCount = newGlobalScenes.Count; + //Remove scenes. + for (int i = 0; i < datas.Length; i++) + newGlobalScenes.Remove(datas[i].Name); + + //If any were removed remake globalscenes. + if (startCount != newGlobalScenes.Count) + _globalScenes = newGlobalScenes.ToArray(); + } + + /// + /// Removes GlobalScenes from scenes. + /// + /// + /// + private void RemoveGlobalScenes(List scenes) + { + for (int i = 0; i < scenes.Count; i++) + { + foreach (string gs in _globalScenes) + { + if (gs == scenes[i].name) + { + scenes.RemoveAt(i); + i--; + } + } + } + } + + /// + /// Removes occupied scenes from scenes. + /// + /// + private void RemoveOccupiedScenes(List scenes) + { + for (int i = 0; i < scenes.Count; i++) + { + if (SceneConnections.TryGetValueIL2CPP(scenes[i], out _)) + { + scenes.RemoveAt(i); + i--; + } + } + } + + /// + /// Adds a pending load for a connection. + /// + private void AddPendingLoad(NetworkConnection conn) + { + AddPendingLoad(new NetworkConnection[] { conn }); + } + /// + /// Adds a pending load for a connection. + /// + private void AddPendingLoad(NetworkConnection[] conns) + { + foreach (NetworkConnection c in conns) + { + /* Make sure connection is active. This should always be true + * but perhaps disconnect happened as scene was loading on server + * therefor it cannot be sent to the client. + * Also only authenticated clients can load scenes. */ + if (!c.IsActive || !c.Authenticated) + continue; + + if (_pendingClientSceneChanges.TryGetValue(c, out int result)) + _pendingClientSceneChanges[c] = (result + 1); + else + _pendingClientSceneChanges[c] = 1; + } + } + /// + /// Sets the first global scene as the active scene. + /// If a global scene is not available then FallbackActiveScene is used. + /// + private void SetActiveScene(Scene preferredScene = default, bool byUser = false) + { + //If user specified then skip figuring it out checks. + if (byUser && preferredScene.IsValid()) + { + CompleteSetActive(preferredScene); + } + //Need to figure out which scene to use. + else + { + Scene s = default; + + //Feature is disabled. + if (!_setActiveScene) + { + //Still call complete to invoke events. + CompleteSetActive(s); + } + else + { + if (_globalScenes != null && _globalScenes.Length > 0) + s = GetScene(_globalScenes[0]); + else if (preferredScene.IsValid()) + s = preferredScene; + + /* If scene isn't set from global then make + * sure currently active isn't the movedobjectscene. + * If it is, then use the fallback scene. */ + if (string.IsNullOrEmpty(s.name) && UnitySceneManager.GetActiveScene() == _movedObjectsScene) + s = GetFallbackActiveScene(); + + CompleteSetActive(s); + } + } + + //Completes setting the active scene with specified value. + void CompleteSetActive(Scene scene) + { + bool sceneValid = scene.IsValid(); + if (sceneValid) + UnitySceneManager.SetActiveScene(scene); + + OnActiveSceneSet?.Invoke(byUser); + OnActiveSceneSetInternal?.Invoke(); + + if (sceneValid) + { + //Also update light probes. + if (_lightProbeUpdating == LightProbeUpdateType.Asynchronous) + LightProbes.TetrahedralizeAsync(); + else if (_lightProbeUpdating == LightProbeUpdateType.BlockThread) + LightProbes.Tetrahedralize(); + } + } + } + + /// + /// Returns the FallbackActiveScene. + /// + /// + private Scene GetFallbackActiveScene() + { + if (string.IsNullOrEmpty(_fallbackActiveScene.name)) + _fallbackActiveScene = UnitySceneManager.CreateScene("FallbackActiveScene"); + + return _fallbackActiveScene; + } + + /// + /// Returns the MovedObejctsScene. + /// + /// + private Scene GetMovedObjectsScene() + { + //Create moved objects scene. It will probably be used eventually. If not, no harm either way. + if (string.IsNullOrEmpty(_movedObjectsScene.name)) + _movedObjectsScene = UnitySceneManager.CreateScene("MovedObjectsHolder"); + + return _movedObjectsScene; + } + + /// + /// Returns the DelayedDestroyScene. + /// + /// + private Scene GetDelayedDestroyScene() + { + //Create moved objects scene. It will probably be used eventually. If not, no harm either way. + if (string.IsNullOrEmpty(_delayedDestroyScene.name)) + _delayedDestroyScene = UnitySceneManager.CreateScene("DelayedDestroy"); + + return _delayedDestroyScene; + } + + /// + /// Returns a preferred active scene to use. + /// + private Scene GetUserPreferredActiveScene(SceneLookupData sld, out bool byUser) + { + byUser = false; + if (sld == null) + return default; + + Scene s = sld.GetScene(out _); + if (s.IsValid()) + byUser = true; + return s; + } + + #region Sanity checks. + /// + /// Returns if a SceneLoadData is valid. + /// + /// + /// + /// + private bool SceneDataInvalid(SceneLoadData data, bool error) + { + bool result = data.DataInvalid(); + if (result && error) + _networkManager.LogError(INVALID_SCENELOADDATA); + + return result; + } + /// + /// Returns if a SceneLoadData is valid. + /// + /// + /// + /// + private bool SceneDataInvalid(SceneUnloadData data, bool error) + { + bool result = data.DataInvalid(); + if (result && error) + _networkManager.LogError(INVALID_SCENEUNLOADDATA); + + + return result; + } + /// + /// Returns if connection is active for server or client in association with AsServer. + /// + /// + /// + private bool ConnectionActive(bool asServer) + { + return (asServer) ? _networkManager.IsServer : _networkManager.IsClient; + } + /// + /// Returns if a method can execute. + /// + /// + /// + /// + private bool CanExecute(bool asServer, bool warn) + { + bool result; + if (asServer) + { + result = _networkManager.IsServer; + if (!result && warn) + _networkManager.LogWarning($"Method cannot be called as the server is not active."); + } + else + { + result = _networkManager.IsClient; + if (!result && warn) + _networkManager.LogWarning($"Method cannot be called as the client is not active."); + } + + return result; + } + #endregion + + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Scened/SceneManager.cs.meta b/Assets/FishNet/Runtime/Managing/Scened/SceneManager.cs.meta new file mode 100644 index 0000000..03dd624 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/SceneManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 15895a51081447d46bda466e7e830c08 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/SceneProcessorBase.cs b/Assets/FishNet/Runtime/Managing/Scened/SceneProcessorBase.cs new file mode 100644 index 0000000..a364c84 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/SceneProcessorBase.cs @@ -0,0 +1,93 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnityScene = UnityEngine.SceneManagement.Scene; + +namespace FishNet.Managing.Scened +{ + + public abstract class SceneProcessorBase : MonoBehaviour + { + #region Protected. + /// + /// SceneManager for this processor. + /// + protected SceneManager SceneManager; + #endregion + + /// + /// Initializes this script for use. + /// + /// SceneManager which will be utilizing this class. + public virtual void Initialize(SceneManager manager) + { + SceneManager = manager; + } + /// + /// Called when scene loading has begun. + /// + public virtual void LoadStart(LoadQueueData queueData) { } + /// + /// Called when scene loading has ended. + /// + public virtual void LoadEnd(LoadQueueData queueData) { } + /// + /// Called when scene unloading has begun within a load operation. + /// + public virtual void UnloadStart(LoadQueueData queueData) { } + /// + /// Called when scene unloading has ended within a load operation. + /// + public virtual void UnloadEnd(LoadQueueData queueData) { } + /// + /// Called when scene unloading has begun within an unload operation. + /// + public virtual void UnloadStart(UnloadQueueData queueData) { } + /// + /// Called when scene unloading has ended within an unload operation. + /// + public virtual void UnloadEnd(UnloadQueueData queueData) { } + /// + /// Begin loading a scene using an async method. + /// + /// Scene name to load. + public abstract void BeginLoadAsync(string sceneName, LoadSceneParameters parameters); + /// + /// Begin unloading a scene using an async method. + /// + /// Scene name to unload. + public abstract void BeginUnloadAsync(Scene scene); + /// + /// Returns if a scene load or unload percent is done. + /// + /// + public abstract bool IsPercentComplete(); + /// + /// Returns the progress on the current scene load or unload. + /// + /// + public abstract float GetPercentComplete(); + /// + /// Adds a scene to loaded scenes. + /// + /// Scene loaded. + public virtual void AddLoadedScene(Scene scene) { } + /// + /// Returns scenes which were loaded during a load operation. + /// + public abstract List GetLoadedScenes(); + /// + /// Activates scenes which were loaded. + /// + public abstract void ActivateLoadedScenes(); + /// + /// Returns if all asynchronized tasks are considered IsDone. + /// + /// + public abstract IEnumerator AsyncsIsDone(); + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Scened/SceneProcessorBase.cs.meta b/Assets/FishNet/Runtime/Managing/Scened/SceneProcessorBase.cs.meta new file mode 100644 index 0000000..e952beb --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/SceneProcessorBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: de3f29952a63dc341a7542a1f898cb12 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/SceneSpawner.cs b/Assets/FishNet/Runtime/Managing/Scened/SceneSpawner.cs new file mode 100644 index 0000000..a0f3d84 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/SceneSpawner.cs @@ -0,0 +1,356 @@ + +//using FishNet.Managing.Scened.Data; +//using System; +//using UnityEngine; +//using UnityEngine.SceneManagement; + +//namespace FishNet.Managing.Scened +//{ + +// public static class SceneSpawner +// { + +// #region Prefab. +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static GameObject Instantiate(Scene scene, GameObject prefab) +// { +// return Instantiate(scene, prefab); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static T Instantiate(Scene scene, GameObject prefab) +// { +// return Instantiate(scene, prefab, prefab.transform.position, prefab.transform.rotation, null, true); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static GameObject Instantiate(SceneReferenceData sceneReferenceData, GameObject prefab) +// { +// return Instantiate(sceneReferenceData, prefab); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static T Instantiate(SceneReferenceData sceneReferenceData, GameObject prefab) +// { +// Scene scene = SceneManager.ReturnScene(sceneReferenceData); +// return Instantiate(scene, prefab, prefab.transform.position, prefab.transform.rotation, null, true); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static GameObject Instantiate(int sceneHandle, GameObject prefab) +// { +// return Instantiate(sceneHandle, prefab, prefab.transform.position, prefab.transform.rotation, null); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static T Instantiate(int sceneHandle, GameObject prefab) +// { +// Scene scene = SceneManager.ReturnScene(sceneHandle); +// return Instantiate(scene, prefab, prefab.transform.position, prefab.transform.rotation, null, true); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static GameObject Instantiate(string sceneName, GameObject prefab) +// { +// return Instantiate(sceneName, prefab); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static T Instantiate(string sceneName, GameObject prefab) +// { +// Scene scene = SceneManager.ReturnScene(sceneName); +// return Instantiate(scene, prefab, prefab.transform.position, prefab.transform.rotation, null, true); +// } +// #endregion + + + + +// #region Prefab, Parent, WorldSpace +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static GameObject Instantiate(Scene scene, GameObject prefab, Transform parent, bool instantiateInWorldSpace = true) +// { +// return Instantiate(scene, prefab, parent, instantiateInWorldSpace); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static T Instantiate(Scene scene, GameObject prefab, Transform parent, bool instantiateInWorldSpace = true) +// { +// return Instantiate(scene, prefab, prefab.transform.position, prefab.transform.rotation, parent, instantiateInWorldSpace); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static GameObject Instantiate(SceneReferenceData sceneReferenceData, GameObject prefab, Transform parent, bool instantiateInWorldSpace = true) +// { +// return Instantiate(sceneReferenceData, prefab, parent, instantiateInWorldSpace); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static T Instantiate(SceneReferenceData sceneReferenceData, GameObject prefab, Transform parent, bool instantiateInWorldSpace = true) +// { +// Scene scene = SceneManager.ReturnScene(sceneReferenceData); +// return Instantiate(scene, prefab, prefab.transform.position, prefab.transform.rotation, parent, instantiateInWorldSpace); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static GameObject Instantiate(int sceneHandle, GameObject prefab, Transform parent, bool instantiateInWorldSpace = true) +// { +// return Instantiate(sceneHandle, prefab, parent, instantiateInWorldSpace); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static T Instantiate(int sceneHandle, GameObject prefab, Transform parent, bool instantiateInWorldSpace = true) +// { +// Scene scene = SceneManager.ReturnScene(sceneHandle); +// return Instantiate(scene, prefab, prefab.transform.position, prefab.transform.rotation, parent, instantiateInWorldSpace); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static GameObject Instantiate(string sceneName, GameObject prefab, Transform parent, bool instantiateInWorldSpace = true) +// { +// return Instantiate(sceneName, prefab, parent, instantiateInWorldSpace); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static T Instantiate(string sceneName, GameObject prefab, Transform parent, bool instantiateInWorldSpace = true) +// { +// Scene scene = SceneManager.ReturnScene(sceneName); +// return Instantiate(scene, prefab, prefab.transform.position, prefab.transform.rotation, parent, instantiateInWorldSpace); +// } +// #endregion + + + + +// #region Prefab, Position, Rotation. +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static GameObject Instantiate(Scene scene, GameObject prefab, Vector3 position, Quaternion rotation) +// { +// return Instantiate(scene, prefab, position, rotation); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static T Instantiate(Scene scene, GameObject prefab, Vector3 position, Quaternion rotation) +// { +// return Instantiate(scene, prefab, position, rotation, null, true); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static GameObject Instantiate(SceneReferenceData sceneReferenceData, GameObject prefab, Vector3 position, Quaternion rotation) +// { +// return Instantiate(sceneReferenceData, prefab, position, rotation); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static T Instantiate(SceneReferenceData sceneReferenceData, GameObject prefab, Vector3 position, Quaternion rotation) +// { +// Scene scene = SceneManager.ReturnScene(sceneReferenceData); +// return Instantiate(scene, prefab, position, rotation, null, true); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static GameObject Instantiate(int sceneHandle, GameObject prefab, Vector3 position, Quaternion rotation) +// { +// return Instantiate(sceneHandle, prefab, position, rotation); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static T Instantiate(int sceneHandle, GameObject prefab, Vector3 position, Quaternion rotation) +// { +// Scene scene = SceneManager.ReturnScene(sceneHandle); +// return Instantiate(scene, prefab, position, rotation, null, true); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static GameObject Instantiate(string sceneName, GameObject prefab, Vector3 position, Quaternion rotation) +// { +// return Instantiate(sceneName, prefab, position, rotation); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static T Instantiate(string sceneName, GameObject prefab, Vector3 position, Quaternion rotation) +// { +// Scene scene = SceneManager.ReturnScene(sceneName); +// return Instantiate(scene, prefab, position, rotation, null, true); +// } +// #endregion + + + + +// #region Prefab, Position, Rotation, Parent. +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static GameObject Instantiate(Scene scene, GameObject prefab, Vector3 position, Quaternion rotation, Transform parent) +// { +// return Instantiate(scene, prefab, position, rotation, parent); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static T Instantiate(Scene scene, GameObject prefab, Vector3 position, Quaternion rotation, Transform parent) +// { +// return Instantiate(scene, prefab, position, rotation, parent, true); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static GameObject Instantiate(SceneReferenceData sceneReferenceData, GameObject prefab, Vector3 position, Quaternion rotation, Transform parent) +// { +// return Instantiate(sceneReferenceData, prefab, position, rotation, parent); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static T Instantiate(SceneReferenceData sceneReferenceData, GameObject prefab, Vector3 position, Quaternion rotation, Transform parent) +// { +// Scene scene = SceneManager.ReturnScene(sceneReferenceData); +// return Instantiate(scene, prefab, position, rotation, parent, true); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static GameObject Instantiate(int sceneHandle, GameObject prefab, Vector3 position, Quaternion rotation, Transform parent) +// { +// return Instantiate(sceneHandle, prefab, position, rotation, parent); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static T Instantiate(int sceneHandle, GameObject prefab, Vector3 position, Quaternion rotation, Transform parent) +// { +// Scene scene = SceneManager.ReturnScene(sceneHandle); +// return Instantiate(scene, prefab, position, rotation, parent, true); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static GameObject Instantiate(string sceneName, GameObject prefab, Vector3 position, Quaternion rotation, Transform parent) +// { +// return Instantiate(sceneName, prefab, position, rotation, parent); +// } +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// public static T Instantiate(string sceneName, GameObject prefab, Vector3 position, Quaternion rotation, Transform parent) +// { +// Scene scene = SceneManager.ReturnScene(sceneName); +// return Instantiate(scene, prefab, position, rotation, parent, true); +// } +// #endregion + + +// #region Instantiator. +// /// +// /// Instantiates a prefab and moves it to a scene. +// /// +// /// Instantiated prefab or script. +// private static T Instantiate(Scene scene, GameObject prefab, Vector3 position, Quaternion rotation, Transform parent, bool instantiateInWorldSpace) +// { +// if (string.IsNullOrEmpty(scene.name)) +// { +// Debug.LogWarning("Scene does not exist. Prefab cannot be instantiated."); +// return default(T); +// } + +// GameObject result = MonoBehaviour.Instantiate(prefab, position, rotation); +// if (result != null) +// { +// //Move to new scene first. +// UnityEngine.SceneManagement.SceneManager.MoveGameObjectToScene(result, scene); + +// //Set parent and spaces. +// if (parent != null) +// { +// result.transform.SetParent(parent); +// //If to not instantiate in world space then update pos/rot to localspace. +// if (!instantiateInWorldSpace) +// { +// result.transform.localPosition = position; +// result.transform.localRotation = rotation; +// } +// } + +// //If was a gameobject then return as GO. +// if (typeof(T) == typeof(GameObject)) +// return (T)Convert.ChangeType(result, typeof(GameObject)); +// //Otherwise use getcomponent on the type. +// else +// return result.GetComponent(); +// } +// //Couldn't be instantiated, return default of T. +// else +// { +// return default(T); +// } + +// } +// #endregion + + +// } + + + + +//} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Scened/SceneSpawner.cs.meta b/Assets/FishNet/Runtime/Managing/Scened/SceneSpawner.cs.meta new file mode 100644 index 0000000..94dd0f6 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/SceneSpawner.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 405b031a6ef64b346ae8c5ccbf07d8e1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Scened/UnloadedScene.cs b/Assets/FishNet/Runtime/Managing/Scened/UnloadedScene.cs new file mode 100644 index 0000000..14bcab1 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/UnloadedScene.cs @@ -0,0 +1,21 @@ +using UnityEngine.SceneManagement; + +namespace FishNet.Managing.Scened +{ + public struct UnloadedScene + { + public readonly string Name; + public readonly int Handle; + + public UnloadedScene(Scene s) + { + Name = s.name; + Handle = s.handle; + } + public UnloadedScene(string name, int handle) + { + Name = name; + Handle = handle; + } + } +} diff --git a/Assets/FishNet/Runtime/Managing/Scened/UnloadedScene.cs.meta b/Assets/FishNet/Runtime/Managing/Scened/UnloadedScene.cs.meta new file mode 100644 index 0000000..c1c66be --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/UnloadedScene.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a51fea764b1081c4ab6308101f160f10 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Server.meta b/Assets/FishNet/Runtime/Managing/Server.meta new file mode 100644 index 0000000..da71fec --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 993fee93506dc1a409dfc0d0cb89354d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Server/ClientConnectionBroadcast.cs b/Assets/FishNet/Runtime/Managing/Server/ClientConnectionBroadcast.cs new file mode 100644 index 0000000..fe7c454 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/ClientConnectionBroadcast.cs @@ -0,0 +1,18 @@ +using FishNet.Broadcast; +using FishNet.Utility.Performance; + +namespace FishNet.Managing.Server +{ + public struct ClientConnectionChangeBroadcast : IBroadcast + { + public bool Connected; + public int Id; + } + + public struct ConnectedClientsBroadcast : IBroadcast + { + public ListCache ListCache; + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Server/ClientConnectionBroadcast.cs.meta b/Assets/FishNet/Runtime/Managing/Server/ClientConnectionBroadcast.cs.meta new file mode 100644 index 0000000..9b30fbf --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/ClientConnectionBroadcast.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a16b83a545be8f8488795783a0fc8648 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Server/Editor.meta b/Assets/FishNet/Runtime/Managing/Server/Editor.meta new file mode 100644 index 0000000..10e9aa6 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: eef52ab3fdb83f04592a1de5ccb741f5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Server/Editor/ServerManagerEditor.cs b/Assets/FishNet/Runtime/Managing/Server/Editor/ServerManagerEditor.cs new file mode 100644 index 0000000..d6de662 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/Editor/ServerManagerEditor.cs @@ -0,0 +1,64 @@ +#if UNITY_EDITOR +using UnityEditor; +using UnityEngine; + +namespace FishNet.Managing.Server.Editing +{ + + + [CustomEditor(typeof(ServerManager), true)] + [CanEditMultipleObjects] + public class ServerManagerEditor : Editor + { + private SerializedProperty _authenticator; + private SerializedProperty _syncTypeRate; + private SerializedProperty SpawnPacking; + private SerializedProperty _changeFrameRate; + private SerializedProperty _frameRate; + private SerializedProperty _shareIds; + private SerializedProperty _startOnHeadless; + private SerializedProperty _limitClientMTU; + + protected virtual void OnEnable() + { + _authenticator = serializedObject.FindProperty(nameof(_authenticator)); + _syncTypeRate = serializedObject.FindProperty(nameof(_syncTypeRate)); + SpawnPacking = serializedObject.FindProperty(nameof(SpawnPacking)); + _changeFrameRate = serializedObject.FindProperty(nameof(_changeFrameRate)); + _frameRate = serializedObject.FindProperty(nameof(_frameRate)); + _shareIds = serializedObject.FindProperty(nameof(_shareIds)); + _startOnHeadless = serializedObject.FindProperty(nameof(_startOnHeadless)); + _limitClientMTU = serializedObject.FindProperty(nameof(_limitClientMTU)); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + GUI.enabled = false; + EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour((ServerManager)target), typeof(ServerManager), false); + GUI.enabled = true; + + + EditorGUILayout.PropertyField(_authenticator); + EditorGUILayout.PropertyField(_syncTypeRate); + EditorGUILayout.PropertyField(SpawnPacking); + EditorGUILayout.PropertyField(_changeFrameRate); + if (_changeFrameRate.boolValue) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_frameRate); + EditorGUI.indentLevel--; + } + EditorGUILayout.PropertyField(_shareIds); + EditorGUILayout.PropertyField(_startOnHeadless); + EditorGUILayout.PropertyField(_limitClientMTU); + + EditorGUILayout.Space(); + + serializedObject.ApplyModifiedProperties(); + } + + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Server/Editor/ServerManagerEditor.cs.meta b/Assets/FishNet/Runtime/Managing/Server/Editor/ServerManagerEditor.cs.meta new file mode 100644 index 0000000..953fe5a --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/Editor/ServerManagerEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: da6ea97e6b868974e8ac139fe545e986 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Server/KickReasons.cs b/Assets/FishNet/Runtime/Managing/Server/KickReasons.cs new file mode 100644 index 0000000..69050c8 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/KickReasons.cs @@ -0,0 +1,36 @@ +namespace FishNet.Managing.Server +{ + + public enum KickReason : short + { + /// + /// No reason was specified. + /// + Unset = 0, + /// + /// Client performed an action which could only be done if trying to exploit the server. + /// + ExploitAttempt = 1, + /// + /// Data received from the client could not be parsed. This rarely indicates an attack. + /// + MalformedData = 2, + /// + /// Client sent more data than should be able to. + /// + ExploitExcessiveData = 3, + /// + /// Client has sent a large amount of data several times in a row. This may not be an attack but there is no way to know with certainty. + /// + ExcessiveData = 4, + /// + /// A problem occurred with the server where the only option was to kick the client. This rarely indicates an exploit attempt. + /// + UnexpectedProblem = 5, + /// + /// Client is behaving unusually, such as providing multiple invalid states. This may not be an attack but there is no way to know with certainty. + /// + UnusualActivity = 6, + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Server/KickReasons.cs.meta b/Assets/FishNet/Runtime/Managing/Server/KickReasons.cs.meta new file mode 100644 index 0000000..af8a9fe --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/KickReasons.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9289a823878c5d1408e7106e6ed5d866 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Server/Object.meta b/Assets/FishNet/Runtime/Managing/Server/Object.meta new file mode 100644 index 0000000..f453896 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/Object.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 272e053307699c84d9787bfd86d3529a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Observers.cs b/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Observers.cs new file mode 100644 index 0000000..3d66b75 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Observers.cs @@ -0,0 +1,522 @@ +using FishNet.Connection; +using FishNet.Managing.Object; +using FishNet.Managing.Transporting; +using FishNet.Object; +using FishNet.Observing; +using FishNet.Serializing; +using FishNet.Transporting; +using FishNet.Utility.Performance; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Managing.Server +{ + public partial class ServerObjects : ManagedObjects + { + #region Private. + /// + /// Cache filled with objects which observers are being updated. + /// This is primarily used to invoke events after all observers are updated, rather than as each is updated. + /// + private List _observerChangedObjectsCache = new List(100); + /// + /// NetworkObservers which require regularly iteration. + /// + private List _timedNetworkObservers = new List(); + /// + /// Index in TimedNetworkObservers to start on next cycle. + /// + private int _nextTimedObserversIndex; + #endregion + + /// + /// Called when MonoBehaviours call Update. + /// + private void Observers_OnUpdate() + { + UpdateTimedObservers(); + } + + /// + /// Progressively updates NetworkObservers with timed conditions. + /// + private void UpdateTimedObservers() + { + if (!base.NetworkManager.IsServer) + return; + //No point in updating if the timemanager isn't going to tick this frame. + if (!base.NetworkManager.TimeManager.FrameTicked) + return; + int observersCount = _timedNetworkObservers.Count; + if (observersCount == 0) + return; + + ServerManager serverManager = base.NetworkManager.ServerManager; + TransportManager transportManager = NetworkManager.TransportManager; + /* Try to iterate all timed observers every half a second. + * This value will increase as there's more observers. */ + int completionTicks = (base.NetworkManager.TimeManager.TickRate * 2); + /* Multiply required ticks based on connection count and nob count. This will + * reduce how quickly observers update slightly but will drastically + * improve performance. */ + float tickMultiplier = 1f + (float)( + (serverManager.Clients.Count * 0.005f) + + (_timedNetworkObservers.Count * 0.0005f) + ); + /* Add an additional iteration to prevent + * 0 iterations */ + int iterations = (observersCount / (int)(completionTicks * tickMultiplier)) + 1; + if (iterations > observersCount) + iterations = observersCount; + + PooledWriter everyoneWriter = WriterPool.GetWriter(); + PooledWriter ownerWriter = WriterPool.GetWriter(); + + //Index to perform a check on. + int observerIndex = 0; + foreach (NetworkConnection conn in serverManager.Clients.Values) + { + int cacheIndex = 0; + using (PooledWriter largeWriter = WriterPool.GetWriter()) + { + //Reset index to start on for every connection. + observerIndex = 0; + /* Run the number of calculated iterations. + * This is spaced out over frames to prevent + * fps spikes. */ + for (int i = 0; i < iterations; i++) + { + observerIndex = _nextTimedObserversIndex + i; + /* Compare actual collection size not cached value. + * This is incase collection is modified during runtime. */ + if (observerIndex >= _timedNetworkObservers.Count) + observerIndex -= _timedNetworkObservers.Count; + + /* If still out of bounds something whack is going on. + * Reset index and exit method. Let it sort itself out + * next iteration. */ + if (observerIndex < 0 || observerIndex >= _timedNetworkObservers.Count) + { + _nextTimedObserversIndex = 0; + break; + } + + NetworkObject nob = _timedNetworkObservers[observerIndex]; + ObserverStateChange osc = nob.RebuildObservers(conn, true); + if (osc == ObserverStateChange.Added) + { + everyoneWriter.Reset(); + ownerWriter.Reset(); + base.WriteSpawn_Server(nob, conn, everyoneWriter, ownerWriter); + CacheObserverChange(nob, ref cacheIndex); + } + else if (osc == ObserverStateChange.Removed) + { + everyoneWriter.Reset(); + WriteDespawn(nob, nob.GetDefaultDespawnType(), everyoneWriter); + + } + else + { + continue; + } + /* Only use ownerWriter if an add, and if owner. Owner + * doesn't matter if not being added because no owner specific + * information would be included. */ + PooledWriter writerToUse = (osc == ObserverStateChange.Added && nob.Owner == conn) ? + ownerWriter : everyoneWriter; + + largeWriter.WriteArraySegment(writerToUse.GetArraySegment()); + } + + if (largeWriter.Length > 0) + { + transportManager.SendToClient( + (byte)Channel.Reliable, + largeWriter.GetArraySegment(), conn); + } + + //Invoke spawn callbacks on nobs. + for (int i = 0; i < cacheIndex; i++) + _observerChangedObjectsCache[i].InvokePostOnServerStart(conn); + } + } + + everyoneWriter.Dispose(); + ownerWriter.Dispose(); + _nextTimedObserversIndex = (observerIndex + 1); + } + + /// + /// Indicates that a networkObserver component should be updated regularly. This is done automatically. + /// + /// NetworkObject to be updated. + public void AddTimedNetworkObserver(NetworkObject networkObject) + { + _timedNetworkObservers.Add(networkObject); + } + + /// + /// Indicates that a networkObserver component no longer needs to be updated regularly. This is done automatically. + /// + /// NetworkObject to be updated. + public void RemoveTimedNetworkObserver(NetworkObject networkObject) + { + _timedNetworkObservers.Remove(networkObject); + } + + /// + /// Caches an observer change. + /// + /// + private void CacheObserverChange(NetworkObject nob, ref int cacheIndex) + { + /* If this spawn would exceed cache size then + * add instead of set value. */ + if (_observerChangedObjectsCache.Count <= cacheIndex) + _observerChangedObjectsCache.Add(nob); + else + _observerChangedObjectsCache[cacheIndex] = nob; + + cacheIndex++; + } + + /// + /// Removes a connection from observers without synchronizing changes. + /// + /// + private void RemoveFromObserversWithoutSynchronization(NetworkConnection connection) + { + int cacheIndex = 0; + + foreach (NetworkObject nob in Spawned.Values) + { + if (nob.RemoveObserver(connection)) + CacheObserverChange(nob, ref cacheIndex); + } + + //Invoke despawn callbacks on nobs. + for (int i = 0; i < cacheIndex; i++) + _observerChangedObjectsCache[i].InvokeOnServerDespawn(connection); + } + + /// + /// Rebuilds observers on all NetworkObjects for all connections. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RebuildObservers() + { + ListCache nobCache = GetOrderedSpawnedObjects(); + ListCache connCache = ListCaches.GetNetworkConnectionCache(); + foreach (NetworkConnection conn in base.NetworkManager.ServerManager.Clients.Values) + connCache.AddValue(conn); + + RebuildObservers(nobCache, connCache); + ListCaches.StoreCache(nobCache); + ListCaches.StoreCache(connCache); + } + + /// + /// Rebuilds observers on NetworkObjects. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RebuildObservers(NetworkObject[] nobs) + { + int count = nobs.Length; + for (int i = 0; i < count; i++) + RebuildObservers(nobs[i]); + } + + /// + /// Rebuilds observers on NetworkObjects. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RebuildObservers(List nobs) + { + int count = nobs.Count; + for (int i = 0; i < count; i++) + RebuildObservers(nobs[i]); + } + + /// + /// Rebuilds observers on NetworkObjects. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RebuildObservers(ListCache nobs) + { + int count = nobs.Written; + List collection = nobs.Collection; + for (int i = 0; i < count; i++) + RebuildObservers(collection[i]); + } + /// + /// Rebuilds observers on NetworkObjects for connections. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RebuildObservers(ListCache nobs, NetworkConnection conn) + { + RebuildObservers(nobs.Collection, conn, nobs.Written); + } + /// + /// Rebuilds observers on NetworkObjects for connections. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RebuildObservers(ListCache nobs, ListCache conns) + { + int count = nobs.Written; + List collection = nobs.Collection; + for (int i = 0; i < count; i++) + RebuildObservers(collection[i], conns); + } + /// + /// Rebuilds observers on all objects for a connections. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RebuildObservers(ListCache connections) + { + int count = connections.Written; + List collection = connections.Collection; + for (int i = 0; i < count; i++) + RebuildObservers(collection[i]); + } + /// + /// Rebuilds observers on all objects for connections. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RebuildObservers(NetworkConnection[] connections) + { + int count = connections.Length; + for (int i = 0; i < count; i++) + RebuildObservers(connections[i]); + } + /// + /// Rebuilds observers on all objects for connections. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RebuildObservers(List connections) + { + int count = connections.Count; + for (int i = 0; i < count; i++) + RebuildObservers(connections[i]); + } + + /// + /// Rebuilds observers on all NetworkObjects for a connection. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RebuildObservers(NetworkConnection connection) + { + ListCache cache = GetOrderedSpawnedObjects(); + RebuildObservers(cache, connection); + ListCaches.StoreCache(cache); + } + + /// + /// Gets all spawned objects with root objects first. + /// + /// + private ListCache GetOrderedSpawnedObjects() + { + ListCache cache = ListCaches.GetNetworkObjectCache(); + foreach (NetworkObject networkObject in Spawned.Values) + { + if (networkObject.IsNested) + continue; + + //Add nob and children recursively. + AddChildNetworkObjects(networkObject); + } + + void AddChildNetworkObjects(NetworkObject n) + { + cache.AddValue(n); + foreach (NetworkObject nob in n.ChildNetworkObjects) + AddChildNetworkObjects(nob); + } + + return cache; + } + + /// + /// Rebuilds observers for a connection on NetworkObjects. + /// + /// NetworkObjects to rebuild. + /// Connection to rebuild for. + /// Number of iterations to perform collection. Entire collection is iterated when value is -1. + public void RebuildObservers(IEnumerable nobs, NetworkConnection connection, int count = -1) + { + PooledWriter everyoneWriter = WriterPool.GetWriter(); + PooledWriter ownerWriter = WriterPool.GetWriter(); + + //If there's no limit on how many can be written set count to the maximum. + if (count == -1) + count = int.MaxValue; + + int iterations; + int observerCacheIndex; + using (PooledWriter largeWriter = WriterPool.GetWriter()) + { + iterations = 0; + observerCacheIndex = 0; + foreach (NetworkObject n in nobs) + { + iterations++; + if (iterations > count) + break; + + //If observer state changed then write changes. + ObserverStateChange osc = n.RebuildObservers(connection, false); + if (osc == ObserverStateChange.Added) + { + everyoneWriter.Reset(); + ownerWriter.Reset(); + base.WriteSpawn_Server(n, connection, everyoneWriter, ownerWriter); + CacheObserverChange(n, ref observerCacheIndex); + } + else if (osc == ObserverStateChange.Removed) + { + connection.LevelOfDetails.Remove(n); + everyoneWriter.Reset(); + WriteDespawn(n, n.GetDefaultDespawnType(), everyoneWriter); + } + else + { + continue; + } + /* Only use ownerWriter if an add, and if owner. Owner //cleanup see if rebuild timed and this can be joined or reuse methods. + * doesn't matter if not being added because no owner specific + * information would be included. */ + PooledWriter writerToUse = (osc == ObserverStateChange.Added && n.Owner == connection) ? + ownerWriter : everyoneWriter; + + largeWriter.WriteArraySegment(writerToUse.GetArraySegment()); + } + + if (largeWriter.Length > 0) + { + NetworkManager.TransportManager.SendToClient( + (byte)Channel.Reliable, + largeWriter.GetArraySegment(), connection); + } + } + + //Dispose of writers created in this method. + everyoneWriter.Dispose(); + ownerWriter.Dispose(); + + //Invoke spawn callbacks on nobs. + for (int i = 0; i < observerCacheIndex; i++) + _observerChangedObjectsCache[i].InvokePostOnServerStart(connection); + } + + /// + /// Rebuilds observers for connections on a NetworkObject. + /// + private void RebuildObservers(NetworkObject nob, ListCache conns) + { + PooledWriter everyoneWriter = WriterPool.GetWriter(); + PooledWriter ownerWriter = WriterPool.GetWriter(); + + int written = conns.Written; + for (int i = 0; i < written; i++) + { + NetworkConnection conn = conns.Collection[i]; + + everyoneWriter.Reset(); + ownerWriter.Reset(); + //If observer state changed then write changes. + ObserverStateChange osc = nob.RebuildObservers(conn, false); + if (osc == ObserverStateChange.Added) + { + base.WriteSpawn_Server(nob, conn, everyoneWriter, ownerWriter); + } + else if (osc == ObserverStateChange.Removed) + { + conn.LevelOfDetails.Remove(nob); + WriteDespawn(nob, nob.GetDefaultDespawnType(), everyoneWriter); + } + else + { + continue; + } + + /* Only use ownerWriter if an add, and if owner. Owner + * doesn't matter if not being added because no owner specific + * information would be included. */ + PooledWriter writerToUse = (osc == ObserverStateChange.Added && nob.Owner == conn) ? + ownerWriter : everyoneWriter; + + if (writerToUse.Length > 0) + { + NetworkManager.TransportManager.SendToClient( + (byte)Channel.Reliable, + writerToUse.GetArraySegment(), conn); + + //If a spawn is being sent. + if (osc == ObserverStateChange.Added) + nob.InvokePostOnServerStart(conn); + } + + } + + //Dispose of writers created in this method. + everyoneWriter.Dispose(); + ownerWriter.Dispose(); + } + + + /// + /// Rebuilds observers for all connections for a NetworkObject. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RebuildObservers(NetworkObject nob) + { + ListCache cache = ListCaches.GetNetworkConnectionCache(); + foreach (NetworkConnection item in NetworkManager.ServerManager.Clients.Values) + cache.AddValue(item); + + RebuildObservers(nob, cache); + ListCaches.StoreCache(cache); + } + /// + /// Rebuilds observers for a connection on NetworkObject. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void RebuildObservers(NetworkObject nob, NetworkConnection conn) + { + ListCache cache = ListCaches.GetNetworkConnectionCache(); + cache.AddValue(conn); + + RebuildObservers(nob, cache); + ListCaches.StoreCache(cache); + } + /// + /// Rebuilds observers for connections on NetworkObject. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RebuildObservers(NetworkObject networkObject, NetworkConnection[] connections) + { + ListCache cache = ListCaches.GetNetworkConnectionCache(); + cache.AddValues(connections); + RebuildObservers(networkObject, cache); + ListCaches.StoreCache(cache); + } + + /// + /// Rebuilds observers for connections on NetworkObject. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RebuildObservers(NetworkObject networkObject, List connections) + { + ListCache cache = ListCaches.GetNetworkConnectionCache(); + cache.AddValues(connections); + RebuildObservers(networkObject, cache); + ListCaches.StoreCache(cache); + } + + + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Observers.cs.meta b/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Observers.cs.meta new file mode 100644 index 0000000..d184e37 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Observers.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 02d93fa4a653dd64da0bb338b82f4740 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Parsing.cs b/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Parsing.cs new file mode 100644 index 0000000..0b3e199 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Parsing.cs @@ -0,0 +1,45 @@ +using FishNet.Connection; +using FishNet.Managing.Object; +using FishNet.Managing.Utility; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Transporting; +using System.Runtime.CompilerServices; + +namespace FishNet.Managing.Server +{ + public partial class ServerObjects : ManagedObjects + { + + /// + /// Parses a ReplicateRpc. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ParseReplicateRpc(PooledReader reader, NetworkConnection conn, Channel channel) + { + NetworkBehaviour nb = reader.ReadNetworkBehaviour(); + int dataLength = Packets.GetPacketLength((ushort)PacketId.ServerRpc, reader, channel); + + if (nb != null) + nb.OnReplicateRpc(null, reader, conn, channel); + else + SkipDataLength((ushort)PacketId.ServerRpc, reader, dataLength); + } + + /// + /// Parses a ServerRpc. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ParseServerRpc(PooledReader reader, NetworkConnection conn, Channel channel) + { + NetworkBehaviour nb = reader.ReadNetworkBehaviour(); + int dataLength = Packets.GetPacketLength((ushort)PacketId.ServerRpc, reader, channel); + + if (nb != null) + nb.OnServerRpc(reader, conn, channel); + else + SkipDataLength((ushort)PacketId.ServerRpc, reader, dataLength); + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Parsing.cs.meta b/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Parsing.cs.meta new file mode 100644 index 0000000..d684ec7 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Parsing.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3cb6cef520a4ff44bb8c4814e566c5ff +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.cs b/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.cs new file mode 100644 index 0000000..ff8415d --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.cs @@ -0,0 +1,876 @@ +using FishNet.Connection; +using FishNet.Managing.Logging; +using FishNet.Managing.Object; +using FishNet.Managing.Timing; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Transporting; +using FishNet.Utility; +using FishNet.Utility.Extension; +using FishNet.Utility.Performance; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace FishNet.Managing.Server +{ + /// + /// Handles objects and information about objects for the server. See ManagedObjects for inherited options. + /// + public partial class ServerObjects : ManagedObjects + { + #region Public. + /// + /// Called right before client objects are destroyed when a client disconnects. + /// + public event Action OnPreDestroyClientObjects; + #endregion + + #region Internal. + /// + /// Collection of NetworkObjects recently despawned. + /// Key: objectId. + /// Value: despawn tick. + /// This is used primarily to track if client is sending messages for recently despawned objects. + /// Objects are automatically removed after RECENTLY_DESPAWNED_DURATION seconds. + /// + internal Dictionary RecentlyDespawnedIds = new Dictionary(); + #endregion + + #region Private. + /// + /// Cached ObjectIds which may be used when exceeding available ObjectIds. + /// + private Queue _objectIdCache = new Queue(); + /// + /// Returns the ObjectId cache. + /// + /// + internal Queue GetObjectIdCache() => _objectIdCache; + /// + /// NetworkBehaviours which have dirty SyncVars. + /// + private List _dirtySyncVarBehaviours = new List(20); + /// + /// NetworkBehaviours which have dirty SyncObjects. + /// + private List _dirtySyncObjectBehaviours = new List(20); + /// + /// Objects which need to be destroyed next tick. + /// This is needed when running as host so host client will get any final messages for the object before they're destroyed. + /// + private Dictionary _pendingDestroy = new Dictionary(); + /// + /// Scenes which were loaded that need to be setup. + /// + private List<(int, Scene)> _loadedScenes = new List<(int frame, Scene scene)>(); + /// + /// Cache of spawning objects, used for recursively spawning nested NetworkObjects. + /// + private ListCache _spawnCache = new ListCache(); + /// + /// True if one or more scenes are currently loading through the SceneManager. + /// + private bool _scenesLoading; + /// + /// Number of ticks which must pass to clear a recently despawned. + /// + private uint _cleanRecentlyDespawnedMaxTicks => base.NetworkManager.TimeManager.TimeToTicks(30d, TickRounding.RoundUp); + #endregion + + internal ServerObjects(NetworkManager networkManager) + { + base.Initialize(networkManager); + networkManager.SceneManager.OnLoadStart += SceneManager_OnLoadStart; + networkManager.SceneManager.OnActiveSceneSetInternal += SceneManager_OnActiveSceneSet; + networkManager.TimeManager.OnUpdate += TimeManager_OnUpdate; + } + + /// + /// Called when MonoBehaviours call Update. + /// + private void TimeManager_OnUpdate() + { + if (!base.NetworkManager.IsServer) + { + _scenesLoading = false; + _loadedScenes.Clear(); + return; + } + + CleanRecentlyDespawned(); + + if (!_scenesLoading) + IterateLoadedScenes(false); + Observers_OnUpdate(); + } + + #region Checking dirty SyncTypes. + /// + /// Iterates NetworkBehaviours with dirty SyncTypes. + /// + internal void WriteDirtySyncTypes() + { + /* Tells networkbehaviours to check their + * dirty synctypes. */ + IterateCollection(_dirtySyncVarBehaviours, false); + IterateCollection(_dirtySyncObjectBehaviours, true); + + void IterateCollection(List collection, bool isSyncObject) + { + for (int i = 0; i < collection.Count; i++) + { + bool dirtyCleared = collection[i].WriteDirtySyncTypes(isSyncObject); + if (dirtyCleared) + { + collection.RemoveAt(i); + i--; + } + } + } + } + /// + /// Sets that a NetworkBehaviour has a dirty syncVars. + /// + /// + internal void SetDirtySyncType(NetworkBehaviour nb, bool isSyncObject) + { + if (isSyncObject) + _dirtySyncObjectBehaviours.Add(nb); + else + _dirtySyncVarBehaviours.Add(nb); + } + #endregion + + #region Connection Handling. + /// + /// Called when the connection state changes for the local server. + /// + /// + internal void OnServerConnectionState(ServerConnectionStateArgs args) + { + + //If server just connected. + if (args.ConnectionState == LocalConnectionState.Started) + { + /* If there's no servers started besides the one + * that just started then build Ids and setup scene objects. */ + if (base.NetworkManager.ServerManager.OneServerStarted()) + { + BuildObjectIdCache(); + SetupSceneObjects(); + } + } + //Server in anything but started state. + else + { + //If no servers are started then reset. + if (!base.NetworkManager.ServerManager.AnyServerStarted()) + { + base.DespawnWithoutSynchronization(true); + base.SceneObjects.Clear(); + _objectIdCache.Clear(); + base.NetworkManager.ClearClientsCollection(base.NetworkManager.ServerManager.Clients); + } + } + } + + /// + /// Called when a client disconnects. + /// + /// + internal void ClientDisconnected(NetworkConnection connection) + { + RemoveFromObserversWithoutSynchronization(connection); + + OnPreDestroyClientObjects?.Invoke(connection); + + /* A cache is made because the Objects + * collection would end up modified during + * iteration from removing ownership and despawning. */ + ListCache cache = ListCaches.GetNetworkObjectCache(); + foreach (NetworkObject nob in connection.Objects) + cache.AddValue(nob); + + int written = cache.Written; + List collection = cache.Collection; + for (int i = 0; i < written; i++) + { + /* Objects may already be deinitializing when a client disconnects + * because the root object could have been despawned first, and in result + * all child objects would have been recursively despawned. + * + * EG: object is: + * A (nob) + * B (nob) + * + * Both A and B are owned by the client so they will both be + * in collection. Should A despawn first B will recursively despawn + * from it. Then once that finishes and the next index of collection + * is run, which would B, the object B would have already been deinitialized. */ + if (!collection[i].IsDeinitializing) + base.NetworkManager.ServerManager.Despawn(collection[i]); + } + + ListCaches.StoreCache(cache); + } + #endregion + + #region ObjectIds. + /// + /// Builds the ObjectId cache with all possible Ids. + /// + private void BuildObjectIdCache() + { + _objectIdCache.Clear(); + + /* Shuffle Ids to make it more difficult + * for clients to track spawned object + * count. */ + List shuffledCache = new List(); + //Ignore ushort.maxvalue as that indicates null. + for (int i = 0; i < (ushort.MaxValue - 1); i++) + shuffledCache.Add(i); + /* Only shuffle when NOT in editor and not + * development build. + * Debugging could be easier when Ids are ordered. */ +#if !UNITY_EDITOR && !DEVELOPMENT_BUILD + shuffledCache.Shuffle(); +#endif + //Add shuffled to objectIdCache. + //Build Id cache. + int cacheCount = shuffledCache.Count; + for (int i = 0; i < cacheCount; i++) + _objectIdCache.Enqueue(shuffledCache[i]); + } + /// + /// Caches a NetworkObject ObjectId. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void CacheObjectId(NetworkObject nob) + { + if (nob.ObjectId != NetworkObject.UNSET_OBJECTID_VALUE) + CacheObjectId(nob.ObjectId); + } + /// + /// Adds an ObjectId to objectId cache. + /// + /// + internal void CacheObjectId(int id) + { + _objectIdCache.Enqueue(id); + } + + /// + /// Gets the next ObjectId to use for NetworkObjects. + /// + /// + protected internal override int GetNextNetworkObjectId(bool errorCheck = true) + { + //Either something went wrong or user actually managed to spawn ~64K networked objects. + if (_objectIdCache.Count == 0) + { + base.NetworkManager.LogError($"No more available ObjectIds. How the heck did you manage to have {ushort.MaxValue} objects spawned at once?"); + return -1; + } + else + { + return _objectIdCache.Dequeue(); + } + } + #endregion + + #region Initializing Objects In Scenes. + /// + /// Called when a scene load starts. + /// + private void SceneManager_OnLoadStart(Scened.SceneLoadStartEventArgs obj) + { + _scenesLoading = true; + } + /// + /// Called after the active scene has been scene, immediately after scene loads. + /// + private void SceneManager_OnActiveSceneSet() + { + _scenesLoading = false; + IterateLoadedScenes(true); + } + /// + /// Iterates loaded scenes and sets them up. + /// + /// True to ignore the frame restriction when iterating. + internal void IterateLoadedScenes(bool ignoreFrameRestriction) + { + //Not started, clear loaded scenes. + if (!NetworkManager.ServerManager.Started) + _loadedScenes.Clear(); + + for (int i = 0; i < _loadedScenes.Count; i++) + { + (int frame, Scene scene) value = _loadedScenes[i]; + if (ignoreFrameRestriction || (Time.frameCount > value.frame)) + { + SetupSceneObjects(value.scene); + _loadedScenes.RemoveAt(i); + i--; + } + } + } + + /// + /// Called when a scene loads on the server. + /// + /// + /// + protected internal override void SceneManager_sceneLoaded(Scene s, LoadSceneMode arg1) + { + base.SceneManager_sceneLoaded(s, arg1); + + if (!NetworkManager.ServerManager.Started) + return; + //Add to loaded scenes so that they are setup next frame. + _loadedScenes.Add((Time.frameCount, s)); + } + + /// + /// Setup all NetworkObjects in scenes. Should only be called when server is active. + /// + protected internal void SetupSceneObjects() + { + for (int i = 0; i < SceneManager.sceneCount; i++) + SetupSceneObjects(SceneManager.GetSceneAt(i)); + + Scene ddolScene = DDOLFinder.GetDDOL().gameObject.scene; + if (ddolScene.isLoaded) + SetupSceneObjects(ddolScene); + } + + /// + /// Setup NetworkObjects in a scene. Should only be called when server is active. + /// + /// + private void SetupSceneObjects(Scene s) + { + ListCache nobs; + SceneFN.GetSceneNetworkObjects(s, false, out nobs); + + bool isHost = base.NetworkManager.IsHost; + + for (int i = 0; i < nobs.Written; i++) + { + NetworkObject nob = nobs.Collection[i]; + //Only setup if a scene object and not initialzied. + if (nob.IsNetworked && nob.IsSceneObject && nob.IsDeinitializing) + { + base.UpdateNetworkBehavioursForSceneObject(nob, true); + base.AddToSceneObjects(nob); + /* If was active in the editor (before hitting play), or currently active + * then PreInitialize without synchronizing to clients. There is no reason + * to synchronize to clients because the scene just loaded on server, + * which means clients are not yet in the scene. */ + if (nob.ActiveDuringEdit || nob.gameObject.activeInHierarchy) + { + //If not host then object doesn't need to be spawned until a client joins. + if (!isHost) + SetupWithoutSynchronization(nob); + //Otherwise spawn object so observers update for clientHost. + else + SpawnWithoutChecks(nob); + } + } + } + + ListCaches.StoreCache(nobs); + } + + /// + /// Performs setup on a NetworkObject without synchronizing the actions to clients. + /// + /// Override ObjectId to use. + private void SetupWithoutSynchronization(NetworkObject nob, NetworkConnection ownerConnection = null, int? objectId = null) + { + if (nob.IsNetworked) + { + if (objectId == null) + objectId = GetNextNetworkObjectId(); + nob.Preinitialize_Internal(NetworkManager, objectId.Value, ownerConnection, true); + base.AddToSpawned(nob, true); + nob.gameObject.SetActive(true); + nob.Initialize(true, true); + } + } + #endregion + + #region Spawning. + /// + /// Spawns an object over the network. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void Spawn(NetworkObject networkObject, NetworkConnection ownerConnection = null) + { + //Default as false, will change if needed. + bool predictedSpawn = false; + + if (networkObject == null) + { + base.NetworkManager.LogError($"Specified networkObject is null."); + return; + } + if (!NetworkManager.ServerManager.Started) + { + //Neither server nor client are started. + if (!NetworkManager.ClientManager.Started) + { + base.NetworkManager.LogWarning("Cannot spawn object because server nor client are active."); + return; + } + //Server has predicted spawning disabled. + if (!NetworkManager.PredictionManager.GetAllowPredictedSpawning()) + { + base.NetworkManager.LogWarning("Cannot spawn object because server is not active and predicted spawning is not enabled."); + return; + } + //Various predicted spawn checks. + if (!base.CanPredictedSpawn(networkObject, NetworkManager.ClientManager.Connection, ownerConnection, false)) + return; + + predictedSpawn = true; + } + if (!networkObject.gameObject.scene.IsValid()) + { + base.NetworkManager.LogError($"{networkObject.name} is a prefab. You must instantiate the prefab first, then use Spawn on the instantiated copy."); + return; + } + if (ownerConnection != null && ownerConnection.IsActive && !ownerConnection.LoadedStartScenes(!predictedSpawn)) + { + base.NetworkManager.LogWarning($"{networkObject.name} was spawned but it's recommended to not spawn objects for connections until they have loaded start scenes. You can be notified when a connection loads start scenes by using connection.OnLoadedStartScenes on the connection, or SceneManager.OnClientLoadStartScenes."); + } + if (networkObject.IsSpawned) + { + base.NetworkManager.LogWarning($"{networkObject.name} is already spawned."); + return; + } + if (networkObject.ParentNetworkObject != null && !networkObject.ParentNetworkObject.IsSpawned) + { + base.NetworkManager.LogError($"{networkObject.name} cannot be spawned because it has a parent NetworkObject {networkObject.ParentNetworkObject} which is not spawned."); + return; + } + + if (predictedSpawn) + base.NetworkManager.ClientManager.Objects.PredictedSpawn(networkObject, ownerConnection); + else + SpawnWithoutChecks(networkObject, ownerConnection); + } + + /// + /// Spawns networkObject without any checks. + /// + private void SpawnWithoutChecks(NetworkObject networkObject, NetworkConnection ownerConnection = null, int? objectId = null) + { + /* Setup locally without sending to clients. + * When observers are built for the network object + * during initialization spawn messages will + * be sent. */ + networkObject.SetIsNetworked(true); + _spawnCache.AddValue(networkObject); + SetupWithoutSynchronization(networkObject, ownerConnection, objectId); + + foreach (NetworkObject item in networkObject.ChildNetworkObjects) + { + /* Only spawn recursively if the nob state is unset. + * Unset indicates that the nob has not been */ + if (item.gameObject.activeInHierarchy || item.State == NetworkObjectState.Spawned) + SpawnWithoutChecks(item, ownerConnection); + } + + /* Copy to a new cache then reset _spawnCache + * just incase rebuilding observers would lead to + * more additions into _spawnCache. EG: rebuilding + * may result in additional objects being spawned + * for clients and if _spawnCache were not reset + * the same objects would be rebuilt again. This likely + * would not affect anything other than perf but who + * wants that. */ + ListCache spawnCacheCopy = ListCaches.GetNetworkObjectCache(); + spawnCacheCopy.AddValues(_spawnCache); + _spawnCache.Reset(); + //Also rebuild observers for the object so it spawns for others. + RebuildObservers(spawnCacheCopy); + + /* If also client then we need to make sure the object renderers have correct visibility. + * Set visibility based on if the observers contains the clientHost connection. */ + if (NetworkManager.IsClient) + { + int count = spawnCacheCopy.Written; + List collection = spawnCacheCopy.Collection; + for (int i = 0; i < count; i++) + collection[i].SetRenderersVisible(networkObject.Observers.Contains(NetworkManager.ClientManager.Connection)); + } + + ListCaches.StoreCache(spawnCacheCopy); + } + + /// + /// Reads a predicted spawn. + /// + internal void ReadPredictedSpawn(PooledReader reader, NetworkConnection conn) + { + sbyte initializeOrder; + ushort collectionId; + int prefabId; + int objectId = reader.ReadNetworkObjectForSpawn(out initializeOrder, out collectionId, out _); + //If objectId is not within predicted ids for conn. + if (!conn.PredictedObjectIds.Contains(objectId)) + { + reader.Clear(); + conn.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"Connection {conn.ClientId} used predicted spawning with a non-reserved objectId of {objectId}."); + return; + } + + NetworkConnection owner = reader.ReadNetworkConnection(); + SpawnType st = (SpawnType)reader.ReadByte(); + //Not used at the moment. + byte componentIndex = reader.ReadByte(); + + //Read transform values which differ from serialized values. + Vector3? localPosition; + Quaternion? localRotation; + Vector3? localScale; + base.ReadTransformProperties(reader, out localPosition, out localRotation, out localScale); + + NetworkObject nob; + bool isGlobal = false; + if (SpawnTypeEnum.Contains(st, SpawnType.Scene)) + { + ulong sceneId = reader.ReadUInt64(AutoPackType.Unpacked); + nob = base.GetSceneNetworkObject(sceneId); + if (!base.CanPredictedSpawn(nob, conn, owner, true)) + return; + } + else + { + //Not used right now. + SpawnParentType spt = (SpawnParentType)reader.ReadByte(); + prefabId = reader.ReadNetworkObjectId(); + //Invalid prefabId. + if (prefabId == NetworkObject.UNSET_PREFABID_VALUE) + { + reader.Clear(); + conn.Kick(KickReason.UnusualActivity, LoggingType.Common, $"Spawned object has an invalid prefabId of {prefabId}. Make sure all objects which are being spawned over the network are within SpawnableObjects on the NetworkManager. Connection {conn.ClientId} will be kicked immediately."); + return; + } + + PrefabObjects prefabObjects = NetworkManager.GetPrefabObjects(collectionId, false); + //PrefabObjects not found. + if (prefabObjects == null) + { + reader.Clear(); + conn.Kick(KickReason.UnusualActivity, LoggingType.Common, $"PrefabObjects collection is not found for CollectionId {collectionId}. Be sure to add your addressables NetworkObject prefabs to the collection on server and client before attempting to spawn them over the network. Connection {conn.ClientId} will be kicked immediately."); + return; + } + //Check if prefab allows predicted spawning. + NetworkObject nPrefab = prefabObjects.GetObject(true, prefabId); + if (!base.CanPredictedSpawn(nPrefab, conn, owner, true)) + return; + + nob = NetworkManager.GetPooledInstantiated(prefabId, false); + isGlobal = SpawnTypeEnum.Contains(st, SpawnType.InstantiatedGlobal); + } + + Transform t = nob.transform; + //Parenting predicted spawns is not supported yet. + t.SetParent(null, true); + base.GetTransformProperties(localPosition, localRotation, localScale, t, out Vector3 pos, out Quaternion rot, out Vector3 scale); + t.SetLocalPositionRotationAndScale(pos, rot, scale); + nob.SetIsGlobal(isGlobal); + + //Initialize for prediction. + nob.InitializePredictedObject_Server(base.NetworkManager, conn); + + /* Only read sync types if allowed for the object. + * If the client did happen to send synctypes while not allowed + * this will create a parse error on the server, + * resulting in the client being kicked. */ + if (nob.AllowPredictedSyncTypes) + { + ArraySegment syncValues = reader.ReadArraySegmentAndSize(); + PooledReader syncTypeReader = ReaderPool.GetReader(syncValues, base.NetworkManager); + foreach (NetworkBehaviour nb in nob.NetworkBehaviours) + { + //SyncVars. + int length = syncTypeReader.ReadInt32(); + nb.OnSyncType(syncTypeReader, length, false, true); + //SyncObjects + length = syncTypeReader.ReadInt32(); + nb.OnSyncType(syncTypeReader, length, true, true); + } + syncTypeReader.Dispose(); + } + + SpawnWithoutChecks(nob, owner, objectId); + + //Send the spawner a new reservedId. + WriteResponse(true); + //Writes a predicted spawn result to a client. + void WriteResponse(bool success) + { + PooledWriter writer = WriterPool.GetWriter(); + writer.WritePacketId(PacketId.PredictedSpawnResult); + writer.WriteNetworkObjectId(nob.ObjectId); + writer.WriteBoolean(success); + + if (success) + { + Queue objectIdCache = NetworkManager.ServerManager.Objects.GetObjectIdCache(); + //Write next objectId to use. + int invalidId = NetworkObject.UNSET_OBJECTID_VALUE; + int nextId = (objectIdCache.Count > 0) ? objectIdCache.Dequeue() : invalidId; + writer.WriteNetworkObjectId(nextId); + //If nextId is valid then also add it to spawners local cache. + if (nextId != invalidId) + conn.PredictedObjectIds.Enqueue(nextId); + ////Update RPC links. + //foreach (NetworkBehaviour nb in nob.NetworkBehaviours) + // nb.WriteRpcLinks(writer); + } + + conn.SendToClient((byte)Channel.Reliable, writer.GetArraySegment()); + } + + } + #endregion + + #region Despawning. + /// + /// Cleans recently despawned objects. + /// + private void CleanRecentlyDespawned() + { + //Only iterate if frame ticked to save perf. + if (!base.NetworkManager.TimeManager.FrameTicked) + return; + + ListCache intCache = ListCaches.GetIntCache(); + + uint requiredTicks = _cleanRecentlyDespawnedMaxTicks; + uint currentTick = base.NetworkManager.TimeManager.LocalTick; + //Iterate 20, or 5% of the collection, whichever is higher. + int iterations = Mathf.Max(20, (int)(RecentlyDespawnedIds.Count * 0.05f)); + /* Given this is a dictionary there is no gaurantee which order objects are + * added. Because of this it's possible some objects may take much longer to + * be removed. This is okay so long as a consistent chunk of objects are removed + * at a time; eventually all objects will be iterated. */ + int count = 0; + foreach (KeyValuePair kvp in RecentlyDespawnedIds) + { + long result = (currentTick - kvp.Value); + //If enough ticks have passed to remove. + if (result > requiredTicks) + intCache.AddValue(kvp.Key); + + count++; + if (count == iterations) + break; + } + + //Remove cached entries. + List collection = intCache.Collection; + int cCount = collection.Count; + for (int i = 0; i < cCount; i++) + RecentlyDespawnedIds.Remove(collection[i]); + + ListCaches.StoreCache(intCache); + } + /// + /// Returns if an objectId was recently despawned. + /// + /// ObjectId to check. + /// Passed ticks to be within to be considered recently despawned. + /// True if an objectId was despawned with specified number of ticks. + public bool RecentlyDespawned(int objectId, uint ticks) + { + uint despawnTick; + if (!RecentlyDespawnedIds.TryGetValue(objectId, out despawnTick)) + return false; + + return ((NetworkManager.TimeManager.LocalTick - despawnTick) <= ticks); + } + /// + /// Adds to objects pending destroy due to clientHost environment. + /// + /// + internal void AddToPending(NetworkObject nob) + { + _pendingDestroy[nob.ObjectId] = nob; + } + /// + /// Tries to removes objectId from PendingDestroy and returns if successful. + /// + internal bool RemoveFromPending(int objectId) + { + return _pendingDestroy.Remove(objectId); + } + /// + /// Returns a NetworkObject in PendingDestroy. + /// + internal NetworkObject GetFromPending(int objectId) + { + NetworkObject nob; + _pendingDestroy.TryGetValue(objectId, out nob); + return nob; + } + /// + /// Destroys NetworkObjects pending for destruction. + /// + internal void DestroyPending() + { + foreach (NetworkObject item in _pendingDestroy.Values) + { + if (item != null) + MonoBehaviour.Destroy(item.gameObject); + } + + _pendingDestroy.Clear(); + } + + /// + /// Despawns an object over the network. + /// + internal override void Despawn(NetworkObject networkObject, DespawnType despawnType, bool asServer) + { + //Default as false, will change if needed. + bool predictedDespawn = false; + + if (networkObject == null) + { + base.NetworkManager.LogWarning($"NetworkObject cannot be despawned because it is null."); + return; + } + if (networkObject.IsDeinitializing) + { + base.NetworkManager.LogWarning($"Object {networkObject.name} cannot be despawned because it is already deinitializing."); + return; + } + + if (!NetworkManager.ServerManager.Started) + { + //Neither server nor client are started. + if (!NetworkManager.ClientManager.Started) + { + base.NetworkManager.LogWarning("Cannot despawn object because server nor client are active."); + return; + } + //Server has predicted spawning disabled. + if (!NetworkManager.PredictionManager.GetAllowPredictedSpawning()) + { + base.NetworkManager.LogWarning("Cannot despawn object because server is not active and predicted spawning is not enabled."); + return; + } + //Various predicted despawn checks. + if (!base.CanPredictedDespawn(networkObject, NetworkManager.ClientManager.Connection, false)) + return; + + predictedDespawn = true; + } + if (!networkObject.gameObject.scene.IsValid()) + { + base.NetworkManager.LogError($"{networkObject.name} is a prefab. You must instantiate the prefab first, then use Spawn on the instantiated copy."); + return; + } + + if (predictedDespawn) + { + base.NetworkManager.ClientManager.Objects.PredictedDespawn(networkObject); + } + else + { + FinalizeDespawn(networkObject, despawnType); + RecentlyDespawnedIds[networkObject.ObjectId] = base.NetworkManager.TimeManager.LocalTick; + base.Despawn(networkObject, despawnType, asServer); + } + } + + /// + /// Called when a NetworkObject is destroyed without being deactivated first. + /// + /// + internal override void NetworkObjectUnexpectedlyDestroyed(NetworkObject nob, bool asServer) + { + FinalizeDespawn(nob, DespawnType.Destroy); + base.NetworkObjectUnexpectedlyDestroyed(nob, asServer); + } + + /// + /// Finalizes the despawn process. By the time this is called the object is considered unaccessible. + /// + /// + private void FinalizeDespawn(NetworkObject nob, DespawnType despawnType) + { + if (nob != null && nob.ObjectId != NetworkObject.UNSET_OBJECTID_VALUE) + { + nob.WriteDirtySyncTypes(); + WriteDespawnAndSend(nob, despawnType); + CacheObjectId(nob); + } + } + + /// + /// Writes a despawn and sends it to clients. + /// + /// + private void WriteDespawnAndSend(NetworkObject nob, DespawnType despawnType) + { + PooledWriter everyoneWriter = WriterPool.GetWriter(); + WriteDespawn(nob, despawnType, everyoneWriter); + + ArraySegment despawnSegment = everyoneWriter.GetArraySegment(); + + //Add observers to a list cache. + ListCache cache = ListCaches.GetNetworkConnectionCache(); + cache.Reset(); + cache.AddValues(nob.Observers); + int written = cache.Written; + for (int i = 0; i < written; i++) + { + //Invoke ondespawn and send despawn. + NetworkConnection conn = cache.Collection[i]; + nob.InvokeOnServerDespawn(conn); + NetworkManager.TransportManager.SendToClient((byte)Channel.Reliable, despawnSegment, conn); + //Remove from observers. + //nob.Observers.Remove(conn); + } + + everyoneWriter.Dispose(); + ListCaches.StoreCache(cache); + } + /// + /// Reads a predicted despawn. + /// + internal void ReadPredictedDespawn(Reader reader, NetworkConnection conn) + { + NetworkObject nob = reader.ReadNetworkObject(); + + //Maybe server destroyed the object so don't kick if null. + if (nob == null) + { + reader.Clear(); + return; + } + //Does not allow predicted despawning. + if (!nob.AllowPredictedDespawning) + { + reader.Clear(); + conn.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"Connection {conn.ClientId} used predicted despawning for object {nob.name} when it does not support predicted despawning."); + } + + //Despawn object. + nob.Despawn(); + } + #endregion + } + + +} diff --git a/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.cs.meta b/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.cs.meta new file mode 100644 index 0000000..ba10d54 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d5e7f3005cbc7924f99819311c58651a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Server/ServerManager.Broadcast.cs b/Assets/FishNet/Runtime/Managing/Server/ServerManager.Broadcast.cs new file mode 100644 index 0000000..f3404cd --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.Broadcast.cs @@ -0,0 +1,457 @@ +using FishNet.Broadcast; +using FishNet.Broadcast.Helping; +using FishNet.Connection; +using FishNet.Managing.Logging; +using FishNet.Managing.Utility; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Serializing.Helping; +using FishNet.Transporting; +using FishNet.Utility.Extension; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Managing.Server +{ + public sealed partial class ServerManager : MonoBehaviour + { + #region Private. + /// + /// Delegate to read received broadcasts. + /// + /// + /// + private delegate void ClientBroadcastDelegate(NetworkConnection connection, PooledReader reader); + /// + /// Delegates for each key. + /// + private readonly Dictionary> _broadcastHandlers = new Dictionary>(); + /// + /// Delegate targets for each key. + /// + private Dictionary> _handlerTargets = new Dictionary>(); + /// + /// Connections which can be broadcasted to after having excluded removed. + /// + private HashSet _connectionsWithoutExclusions = new HashSet(); + #endregion + + /// + /// Registers a method to call when a Broadcast arrives. + /// + /// Type of broadcast being registered. + /// Method to call. + /// True if the client must be authenticated for the method to call. + public void RegisterBroadcast(Action handler, bool requireAuthentication = true) where T : struct, IBroadcast + { + ushort key = BroadcastHelper.GetKey(); + + /* Create delegate and add for + * handler method. */ + HashSet handlers; + if (!_broadcastHandlers.TryGetValueIL2CPP(key, out handlers)) + { + handlers = new HashSet(); + _broadcastHandlers.Add(key, handlers); + } + ClientBroadcastDelegate del = CreateBroadcastDelegate(handler, requireAuthentication); + handlers.Add(del); + + /* Add hashcode of target for handler. + * This is so we can unregister the target later. */ + int handlerHashCode = handler.GetHashCode(); + HashSet<(int, ClientBroadcastDelegate)> targetHashCodes; + if (!_handlerTargets.TryGetValueIL2CPP(key, out targetHashCodes)) + { + targetHashCodes = new HashSet<(int, ClientBroadcastDelegate)>(); + _handlerTargets.Add(key, targetHashCodes); + } + + targetHashCodes.Add((handlerHashCode, del)); + } + + /// + /// Unregisters a method call from a Broadcast type. + /// + /// Type of broadcast being unregistered. + /// Method to unregister. + public void UnregisterBroadcast(Action handler) where T : struct, IBroadcast + { + ushort key = BroadcastHelper.GetKey(); + + /* If key is found for T then look for + * the appropriate handler to remove. */ + if (_broadcastHandlers.TryGetValueIL2CPP(key, out HashSet handlers)) + { + HashSet<(int, ClientBroadcastDelegate)> targetHashCodes; + if (_handlerTargets.TryGetValueIL2CPP(key, out targetHashCodes)) + { + int handlerHashCode = handler.GetHashCode(); + ClientBroadcastDelegate result = null; + foreach ((int targetHashCode, ClientBroadcastDelegate del) in targetHashCodes) + { + if (targetHashCode == handlerHashCode) + { + result = del; + targetHashCodes.Remove((targetHashCode, del)); + break; + } + } + //If no more in targetHashCodes then remove from handlerTarget. + if (targetHashCodes.Count == 0) + _handlerTargets.Remove(key); + + if (result != null) + handlers.Remove(result); + } + + //If no more in handlers then remove broadcastHandlers. + if (handlers.Count == 0) + _broadcastHandlers.Remove(key); + } + } + + /// + /// Creates a ClientBroadcastDelegate. + /// + /// + /// + /// + /// + private ClientBroadcastDelegate CreateBroadcastDelegate(Action handler, bool requireAuthentication) + { + void LogicContainer(NetworkConnection connection, PooledReader reader) + { + //If requires authentication and client isn't authenticated. + if (requireAuthentication && !connection.Authenticated) + { + connection.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"ConnectionId {connection.ClientId} sent broadcast {typeof(T).Name} which requires authentication, but client was not authenticated. Client has been disconnected."); + return; + } + + T broadcast = reader.Read(); + handler?.Invoke(connection, broadcast); + } + return LogicContainer; + } + + /// + /// Parses a received broadcast. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ParseBroadcast(PooledReader reader, NetworkConnection conn, Channel channel) + { + ushort key = reader.ReadUInt16(); + int dataLength = Packets.GetPacketLength((ushort)PacketId.Broadcast, reader, channel); + + //Try to invoke the handler for that message + if (_broadcastHandlers.TryGetValueIL2CPP(key, out HashSet handlers)) + { + int readerStartPosition = reader.Position; + /* //muchlater resetting the position could be better by instead reading once and passing in + * the object to invoke with. */ + bool rebuildHandlers = false; + //True if data is read at least once. Otherwise it's length will have to be purged. + bool dataRead = false; + foreach (ClientBroadcastDelegate handler in handlers) + { + if (handler.Target == null) + { + NetworkManager.LogWarning($"A Broadcast handler target is null. This can occur when a script is destroyed but does not unregister from a Broadcast."); + rebuildHandlers = true; + } + else + { + reader.Position = readerStartPosition; + handler.Invoke(conn, reader); + dataRead = true; + } + } + + //If rebuilding handlers... + if (rebuildHandlers) + { + List dels = handlers.ToList(); + handlers.Clear(); + for (int i = 0; i < dels.Count; i++) + { + if (dels[i].Target != null) + handlers.Add(dels[i]); + } + } + //Make sure data was read as well. + if (!dataRead) + reader.Skip(dataLength); + } + else + { + reader.Skip(dataLength); + } + } + + /// + /// Sends a broadcast to a connection. + /// + /// Type of broadcast to send. + /// Connection to send to. + /// Broadcast data being sent; for example: an instance of your broadcast type. + /// True if the client must be authenticated for this broadcast to send. + /// Channel to send on. + public void Broadcast(NetworkConnection connection, T message, bool requireAuthenticated = true, Channel channel = Channel.Reliable) where T : struct, IBroadcast + { + if (!Started) + { + NetworkManager.LogWarning($"Cannot send broadcast to client because server is not active."); + return; + } + if (requireAuthenticated && !connection.Authenticated) + { + NetworkManager.LogWarning($"Cannot send broadcast to client because they are not authenticated."); + return; + } + + using (PooledWriter writer = WriterPool.GetWriter()) + { + Broadcasts.WriteBroadcast(writer, message, channel); + ArraySegment segment = writer.GetArraySegment(); + NetworkManager.TransportManager.SendToClient((byte)channel, segment, connection); + } + } + + + /// + /// Sends a broadcast to connections. + /// + /// Type of broadcast to send. + /// Connections to send to. + /// Broadcast data being sent; for example: an instance of your broadcast type. + /// True if the clients must be authenticated for this broadcast to send. + /// Channel to send on. + public void Broadcast(HashSet connections, T message, bool requireAuthenticated = true, Channel channel = Channel.Reliable) where T : struct, IBroadcast + { + if (!Started) + { + NetworkManager.LogWarning($"Cannot send broadcast to client because server is not active."); + return; + } + + bool failedAuthentication = false; + using (PooledWriter writer = WriterPool.GetWriter()) + { + Broadcasts.WriteBroadcast(writer, message, channel); + ArraySegment segment = writer.GetArraySegment(); + + foreach (NetworkConnection conn in connections) + { + if (requireAuthenticated && !conn.Authenticated) + failedAuthentication = true; + else + NetworkManager.TransportManager.SendToClient((byte)channel, segment, conn); + } + } + + if (failedAuthentication) + { + NetworkManager.LogWarning($"One or more broadcast did not send to a client because they were not authenticated."); + return; + } + } + + + /// + /// Sends a broadcast to connections except excluded. + /// + /// Type of broadcast to send. + /// Connections to send to. + /// Connection to exclude. + /// Broadcast data being sent; for example: an instance of your broadcast type. + /// True if the clients must be authenticated for this broadcast to send. + /// Channel to send on. + public void BroadcastExcept(HashSet connections, NetworkConnection excludedConnection, T message, bool requireAuthenticated = true, Channel channel = Channel.Reliable) where T : struct, IBroadcast + { + if (!Started) + { + NetworkManager.LogWarning($"Cannot send broadcast to client because server is not active."); + return; + } + + //Fast exit if no exclusions. + if (excludedConnection == null || !excludedConnection.IsValid) + { + Broadcast(connections, message, requireAuthenticated, channel); + return; + } + + connections.Remove(excludedConnection); + Broadcast(connections, message, requireAuthenticated, channel); + } + + + /// + /// Sends a broadcast to connections except excluded. + /// + /// Type of broadcast to send. + /// Connections to send to. + /// Connections to exclude. + /// Broadcast data being sent; for example: an instance of your broadcast type. + /// True if the clients must be authenticated for this broadcast to send. + /// Channel to send on. + public void BroadcastExcept(HashSet connections, HashSet excludedConnections, T message, bool requireAuthenticated = true, Channel channel = Channel.Reliable) where T : struct, IBroadcast + { + if (!Started) + { + NetworkManager.LogWarning($"Cannot send broadcast to client because server is not active."); + return; + } + + //Fast exit if no exclusions. + if (excludedConnections == null || excludedConnections.Count == 0) + { + Broadcast(connections, message, requireAuthenticated, channel); + return; + } + + /* I'm not sure if the hashset API such as intersect generates + * GC or not but I'm betting doing remove locally is faster, or + * just as fast. */ + foreach (NetworkConnection ec in excludedConnections) + connections.Remove(ec); + + Broadcast(connections, message, requireAuthenticated, channel); + } + + /// + /// Sends a broadcast to all connections except excluded. + /// + /// Type of broadcast to send. + /// Connection to exclude. + /// Broadcast data being sent; for example: an instance of your broadcast type. + /// True if the clients must be authenticated for this broadcast to send. + /// Channel to send on. + public void BroadcastExcept(NetworkConnection excludedConnection, T message, bool requireAuthenticated = true, Channel channel = Channel.Reliable) where T : struct, IBroadcast + { + if (!Started) + { + NetworkManager.LogWarning($"Cannot send broadcast to client because server is not active."); + return; + } + + //Fast exit if there are no excluded. + if (excludedConnection == null || !excludedConnection.IsValid) + { + Broadcast(message, requireAuthenticated, channel); + return; + } + + _connectionsWithoutExclusions.Clear(); + /* It will be faster to fill the entire list then + * remove vs checking if each connection is contained within excluded. */ + foreach (NetworkConnection c in Clients.Values) + _connectionsWithoutExclusions.Add(c); + //Remove + _connectionsWithoutExclusions.Remove(excludedConnection); + + Broadcast(_connectionsWithoutExclusions, message, requireAuthenticated, channel); + } + + /// + /// Sends a broadcast to all connections except excluded. + /// + /// Type of broadcast to send. + /// Connections to send to. + /// Broadcast data being sent; for example: an instance of your broadcast type. + /// True if the clients must be authenticated for this broadcast to send. + /// Channel to send on. + public void BroadcastExcept(HashSet excludedConnections, T message, bool requireAuthenticated = true, Channel channel = Channel.Reliable) where T : struct, IBroadcast + { + if (!Started) + { + NetworkManager.LogWarning($"Cannot send broadcast to client because server is not active."); + return; + } + + //Fast exit if there are no excluded. + if (excludedConnections == null || excludedConnections.Count == 0) + { + Broadcast(message, requireAuthenticated, channel); + return; + } + + _connectionsWithoutExclusions.Clear(); + /* It will be faster to fill the entire list then + * remove vs checking if each connection is contained within excluded. */ + foreach (NetworkConnection c in Clients.Values) + _connectionsWithoutExclusions.Add(c); + //Remove + foreach (NetworkConnection c in excludedConnections) + _connectionsWithoutExclusions.Remove(c); + + Broadcast(_connectionsWithoutExclusions, message, requireAuthenticated, channel); + } + + /// + /// Sends a broadcast to observers. + /// + /// Type of broadcast to send. + /// NetworkObject to use Observers from. + /// Broadcast data being sent; for example: an instance of your broadcast type. + /// True if the clients must be authenticated for this broadcast to send. + /// Channel to send on. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Broadcast(NetworkObject networkObject, T message, bool requireAuthenticated = true, Channel channel = Channel.Reliable) where T : struct, IBroadcast + { + if (networkObject == null) + { + NetworkManager.LogWarning($"Cannot send broadcast because networkObject is null."); + return; + } + + Broadcast(networkObject.Observers, message, requireAuthenticated, channel); + } + + + /// + /// Sends a broadcast to all clients. + /// + /// Type of broadcast to send. + /// Broadcast data being sent; for example: an instance of your broadcast type. + /// True if the clients must be authenticated for this broadcast to send. + /// Channel to send on. + public void Broadcast(T message, bool requireAuthenticated = true, Channel channel = Channel.Reliable) where T : struct, IBroadcast + { + if (!Started) + { + NetworkManager.LogWarning($"Cannot send broadcast to client because server is not active."); + return; + } + + bool failedAuthentication = false; + using (PooledWriter writer = WriterPool.GetWriter()) + { + Broadcasts.WriteBroadcast(writer, message, channel); + ArraySegment segment = writer.GetArraySegment(); + + foreach (NetworkConnection conn in Clients.Values) + { + // + if (requireAuthenticated && !conn.Authenticated) + failedAuthentication = true; + else + NetworkManager.TransportManager.SendToClient((byte)channel, segment, conn); + } + } + + if (failedAuthentication) + { + NetworkManager.LogWarning($"One or more broadcast did not send to a client because they were not authenticated."); + return; + } + } + + } + + +} diff --git a/Assets/FishNet/Runtime/Managing/Server/ServerManager.Broadcast.cs.meta b/Assets/FishNet/Runtime/Managing/Server/ServerManager.Broadcast.cs.meta new file mode 100644 index 0000000..e0fdef7 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.Broadcast.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1cd9dcd58556e27449ce5cb0d70611cb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Server/ServerManager.LOD.cs b/Assets/FishNet/Runtime/Managing/Server/ServerManager.LOD.cs new file mode 100644 index 0000000..7829e2a --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.LOD.cs @@ -0,0 +1,231 @@ +using FishNet.Connection; +using FishNet.Managing.Logging; +using FishNet.Object; +using FishNet.Serializing; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Managing.Server +{ + public sealed partial class ServerManager : MonoBehaviour + { + #region Private. + /// + /// Cached expected level of detail value. + /// + private uint _cachedLevelOfDetailInterval; + /// + /// Cached value of UseLod. + /// + private bool _cachedUseLod; + #endregion + + /// + /// Parses a received network LOD update. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ParseNetworkLODUpdate(PooledReader reader, NetworkConnection conn) + { + if (!conn.Authenticated) + return; + if (!NetworkManager.ObserverManager.GetUseNetworkLod()) + { + conn.Kick(reader, KickReason.ExploitAttempt, LoggingType.Common, $"Connection [{conn.ClientId}] sent a level of detail update when the feature is not enabled."); + return; + } + + /* If local client then read out entries but do nothing. + * Local client doesn't technically have to send LOD because + * it's set on the client side but this code is kept in + * to simulate actual bandwidth. */ + if (conn.IsLocalClient) + { + int w = reader.ReadInt32(); + for (int i = 0; i < w; i++) + ReadLod(out _, out _); + return; + } + + uint packetTick = conn.LastPacketTick; + //Check if conn can send LOD. + uint lastLod = conn.LastLevelOfDetailUpdate; + //If previously set see if client is potentially exploiting. + if (lastLod != 0) + { + if ((packetTick - lastLod) < _cachedLevelOfDetailInterval) + { + conn.Kick(reader, KickReason.ExploitAttempt, LoggingType.Common, $"Connection [{conn.ClientId}] sent excessive level of detail updates."); + return; + } + } + //Set last recv lod. + conn.LastLevelOfDetailUpdate = packetTick; + + //Get server objects to save calls. + Dictionary serverObjects = Objects.Spawned; + //Get level of details for this connection and reset them. + Dictionary levelOfDetails = conn.LevelOfDetails; + + int written = reader.ReadInt32(); + + /* //TODO There is still an instance where client could simply say no LODs need + * updating and never update for their objects in the first place. This can be resolved + * by adding an observed object count to each connection and compare that to + * the size of the LOD collection. */ + + //Only process if some are written. + if (written > 0) + { + //Maximum infractions before a kick. + const int maximumInfractions = 15; + int currentInfractions = conn.LevelOfDetailInfractions; + int infractionsCounted = 0; + + /* If the connection has no objects then LOD isn't capable + * of being calculated. It's possible the players object was destroyed after + * the LOD sent but we don't know for sure without adding extra checks. + * Rather than add recently destroyed player object checks if there are + * no player objects then just add an infraction. The odds of this happening regularly + * are pretty slim. */ + if (conn.Objects.Count == 0) + { + if (AddInfraction(3)) + { + conn.Kick(reader, KickReason.UnusualActivity, LoggingType.Common, $"Connection [{conn.ClientId}] has sent an excessive number of level of detail updates without having any player objects spawned."); + return; + } + } + + /* If written is more than spawned + recently despawned then + * the client is likely trying to exploit. */ + if (written > (Objects.Spawned.Count + Objects.RecentlyDespawnedIds.Count)) + { + conn.Kick(reader, KickReason.UnusualActivity, LoggingType.Common, $"Connection [{conn.ClientId}] sent a level of detail update for {written} items which exceeds spawned and recently despawned count."); + return; + } + + Vector3 connObjectPosition = Vector3.zero; + //Pick a random object from the player to sample. + int objectIndex = UnityEngine.Random.Range(0, conn.Objects.Count); + int connObjectIteration = 0; + foreach (NetworkObject n in conn.Objects) + { + if (connObjectIteration == objectIndex) + { + connObjectPosition = n.transform.position; + //Flag to indicate found. + objectIndex = -1; + break; + } + } + //Server somehow messed up. Should not be possible. + if (objectIndex != -1) + { + NetworkManager.LogError($"An object index of {objectIndex} could not be populated. Connection [{conn.ClientId}] object count is {conn.Objects.Count}."); + return; + } + + //Sample at most x entries per update. + int samplesRemaining = 10; + //Chance to sample an update. + const float sampleChance = 0.05f; + + List lodDistances = NetworkManager.ObserverManager.GetLevelOfDetailDistances(); + int lodDistancesCount = lodDistances.Count; + for (int i = 0; i < written; i++) + { + int objectId; + byte lod; + ReadLod(out objectId, out lod); + + //Lod is not possible. + if (lod >= lodDistancesCount) + { + conn.Kick(reader, KickReason.ExploitAttempt, LoggingType.Common, $"Connection [{conn.ClientId}] provided a level of detail index which is out of bounds."); + return; + } + + //Found in spawned, update lod. + if (serverObjects.TryGetValue(objectId, out NetworkObject nob)) + { + //Value is unchanged. + if (levelOfDetails.TryGetValue(nob, out byte oldLod)) + { + bool oldMatches = (oldLod == lod); + if (oldMatches && AddInfraction()) + { + conn.Kick(reader, KickReason.UnusualActivity, LoggingType.Common, $"Connection [{conn.ClientId}] has excessively sent unchanged LOD information."); + return; + } + } + //If to sample. + if (samplesRemaining > 0 && UnityEngine.Random.Range(0f, 1f) <= sampleChance) + { + samplesRemaining--; + /* Only check if lod is less than maximum. + * If the client is hacking lods to specify maximum + * they are only doing the server a favor and hurting + * themselves with slower updates. */ + if (lod < (lodDistancesCount - 1)) + { + float specifiedLodDistance = lodDistances[lod]; + float sqrMag = Vector3.SqrMagnitude(connObjectPosition - nob.transform.position); + /* If the found distance is actually larger than what client specified + * then it's possible client may be sending fake LODs. */ + if (sqrMag > specifiedLodDistance) + { + if (AddInfraction()) + { + conn.Kick(reader, KickReason.UnusualActivity, LoggingType.Common, $"Connection [{conn.ClientId}] provided an excessive number of incorrect LOD values."); + return; + } + } + } + } + + levelOfDetails[nob] = lod; + } + //Not found in spawn; validate that client isn't trying to exploit. + else + { + //Too many infractions. + if (AddInfraction()) + { + conn.Kick(reader, KickReason.UnusualActivity, LoggingType.Common, $"Connection [{conn.ClientId}] has accumulated excessive level of detail infractions."); + return; + } + } + } + + //Adds an infraction returning if maximum infractions have been exceeded. + bool AddInfraction(int count = 1) + { + /* Only increase infractions at most 3 per iteration. + * This is to prevent a kick if the client perhaps had + * a massive lag spike. */ + if (infractionsCounted < 3) + infractionsCounted += count; + + bool overLimit = ((currentInfractions + infractionsCounted) >= maximumInfractions); + return overLimit; + } + } + + //Reads a LOD. + void ReadLod(out int lObjectId, out byte lLod) + { + lObjectId = reader.ReadNetworkObjectId(); + lLod = reader.ReadByte(); + } + + //Remove an infraction. This will steadily remove infractions over time. + if (conn.LevelOfDetailInfractions > 0) + conn.LevelOfDetailInfractions--; + } + + + } + + +} diff --git a/Assets/FishNet/Runtime/Managing/Server/ServerManager.LOD.cs.meta b/Assets/FishNet/Runtime/Managing/Server/ServerManager.LOD.cs.meta new file mode 100644 index 0000000..1868733 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.LOD.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e472255008ae291498f55f5f2a704ab2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Server/ServerManager.QOL.cs b/Assets/FishNet/Runtime/Managing/Server/ServerManager.QOL.cs new file mode 100644 index 0000000..66da54b --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.QOL.cs @@ -0,0 +1,199 @@ +using FishNet.Connection; +using FishNet.Managing.Logging; +using FishNet.Managing.Transporting; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Transporting; +using FishNet.Transporting.Multipass; +using System; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Managing.Server +{ + public sealed partial class ServerManager : MonoBehaviour + { + #region Public. + /// + /// Called when a client is removed from the server using Kick. This is invoked before the client is disconnected. + /// NetworkConnection when available, clientId, and KickReason are provided. + /// + public event Action OnClientKick; + #endregion + + /// + /// Returns true if only one server is started. + /// + /// + public bool OneServerStarted() + { + int startedCount = 0; + TransportManager tm = NetworkManager.TransportManager; + //If using multipass check all transports. + if (tm.Transport is Multipass mp) + { + + foreach (Transport t in mp.Transports) + { + //Another transport is started, no need to load start scenes again. + if (t.GetConnectionState(true) == LocalConnectionState.Started) + startedCount++; + } + } + //Not using multipass. + else + { + if (tm.Transport.GetConnectionState(true) == LocalConnectionState.Started) + startedCount = 1; + } + + return (startedCount == 1); + } + + /// + /// Returns true if any server socket is in the started state. + /// + /// When set the transport on this index will be ignored. This value is only used with Multipass. + /// + public bool AnyServerStarted(int? excludedIndex = null) + { + TransportManager tm = NetworkManager.TransportManager; + //If using multipass check all transports. + if (tm.Transport is Multipass mp) + { + //Get transport which had state changed. + Transport excludedTransport = (excludedIndex == null) ? null : mp.GetTransport(excludedIndex.Value); + + foreach (Transport t in mp.Transports) + { + /* Skip t if is the transport that had it's state changed. + * We are looking for other transports already in started. */ + if (t == excludedTransport) + continue; + //Another transport is started, no need to load start scenes again. + if (t.GetConnectionState(true) == LocalConnectionState.Started) + return true; + } + } + //Not using multipass. + else + { + return (tm.Transport.GetConnectionState(true) == LocalConnectionState.Started); + } + + //Fall through, none started. + return false; + } + + /// + /// Spawns an object over the network. Can only be called on the server. + /// + /// GameObject instance to spawn. + /// Connection to give ownership to. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Spawn(GameObject go, NetworkConnection ownerConnection = null) + { + if (go == null) + { + NetworkManager.LogWarning($"GameObject cannot be spawned because it is null."); + return; + } + + NetworkObject nob = go.GetComponent(); + Spawn(nob, ownerConnection); + } + + + /// + /// Spawns an object over the network. Can only be called on the server. + /// + /// MetworkObject instance to spawn. + /// Connection to give ownership to. + public void Spawn(NetworkObject nob, NetworkConnection ownerConnection = null) + { + Objects.Spawn(nob, ownerConnection); + } + + /// + /// Despawns an object over the network. Can only be called on the server. + /// + /// GameObject instance to despawn. + /// Overrides the default DisableOnDespawn value for this single despawn. Scene objects will never be destroyed. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Despawn(GameObject go, DespawnType? despawnType = null) + { + if (go == null) + { + NetworkManager.LogWarning($"GameObject cannot be despawned because it is null."); + return; + } + + NetworkObject nob = go.GetComponent(); + Despawn(nob, despawnType); + } + + /// + /// Despawns an object over the network. Can only be called on the server. + /// + /// NetworkObject instance to despawn. + /// Despawn override type. + public void Despawn(NetworkObject networkObject, DespawnType? despawnType = null) + { + DespawnType resolvedDespawnType = (despawnType == null) + ? networkObject.GetDefaultDespawnType() + : despawnType.Value; + Objects.Despawn(networkObject, resolvedDespawnType, true); + } + + /// + /// Kicks a connection immediately while invoking OnClientKick. + /// + /// Client to kick. + /// Reason client is being kicked. + /// How to print logging as. + /// Optional message to be debug logged. + public void Kick(NetworkConnection conn, KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "") + { + if (!conn.IsValid) + return; + + OnClientKick?.Invoke(conn, conn.ClientId, kickReason); + if (conn.IsActive) + conn.Disconnect(true); + + if (!string.IsNullOrEmpty(log)) + NetworkManager.Log(loggingType, log); + } + + /// + /// Kicks a connection immediately while invoking OnClientKick. + /// + /// ClientId to kick. + /// Reason client is being kicked. + /// How to print logging as. + /// Optional message to be debug logged. + public void Kick(int clientId, KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "") + { + OnClientKick?.Invoke(null, clientId, kickReason); + NetworkManager.TransportManager.Transport.StopConnection(clientId, true); + if (!string.IsNullOrEmpty(log)) + NetworkManager.Log(loggingType, log); + } + + /// + /// Kicks a connection immediately while invoking OnClientKick. + /// + /// Client to kick. + /// Reader to clear before kicking. + /// Reason client is being kicked. + /// How to print logging as. + /// Optional message to be debug logged. + public void Kick(NetworkConnection conn, Reader reader, KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "") + { + reader.Clear(); + Kick(conn, kickReason, loggingType, log); + } + } + + +} diff --git a/Assets/FishNet/Runtime/Managing/Server/ServerManager.QOL.cs.meta b/Assets/FishNet/Runtime/Managing/Server/ServerManager.QOL.cs.meta new file mode 100644 index 0000000..2ff8199 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.QOL.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f2a07d9984be21648bc714ea03bd0d70 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Server/ServerManager.RpcLinks.cs b/Assets/FishNet/Runtime/Managing/Server/ServerManager.RpcLinks.cs new file mode 100644 index 0000000..b258950 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.RpcLinks.cs @@ -0,0 +1,78 @@ +using FishNet.Object; +using FishNet.Transporting; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace FishNet.Managing.Server +{ + + public sealed partial class ServerManager : MonoBehaviour + { + + + #region Internal + /// + /// Current RPCLinks. + /// + internal Dictionary RpcLinks = new Dictionary(); + /// + /// RPCLink indexes which can be used. + /// + private Queue _availableRpcLinkIndexes = new Queue(); + #endregion + + /// + /// Initializes RPC Links for NetworkBehaviours. + /// + private void InitializeRpcLinks() + { + /* Brute force enum values. + * Linq Last/Max lookup throws for IL2CPP. */ + ushort highestValue = 0; + Array pidValues = Enum.GetValues(typeof(PacketId)); + foreach (PacketId pid in pidValues) + highestValue = Math.Max(highestValue, (ushort)pid); + + highestValue += 1; + for (ushort i = highestValue; i < ushort.MaxValue; i++) + _availableRpcLinkIndexes.Enqueue(i); + } + + /// + /// Sets the next RPC Link to use. + /// + /// True if a link was available and set. + internal bool GetRpcLink(out ushort value) + { + if (_availableRpcLinkIndexes.Count > 0) + { + value = _availableRpcLinkIndexes.Dequeue(); + return true; + } + else + { + value = 0; + return false; + } + } + + /// + /// Sets data to RpcLinks for linkIndex. + /// + internal void SetRpcLink(ushort linkIndex, RpcLink data) + { + RpcLinks[linkIndex] = data; + } + + /// + /// Returns RPCLinks to availableRpcLinkIndexes. + /// + internal void StoreRpcLinks(Dictionary links) + { + foreach (RpcLinkType rlt in links.Values) + _availableRpcLinkIndexes.Enqueue(rlt.LinkIndex); + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Server/ServerManager.RpcLinks.cs.meta b/Assets/FishNet/Runtime/Managing/Server/ServerManager.RpcLinks.cs.meta new file mode 100644 index 0000000..00ff24f --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.RpcLinks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b9f0a4620d06f5c41b01f20af3f90634 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs b/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs new file mode 100644 index 0000000..a474dd3 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs @@ -0,0 +1,759 @@ +using FishNet.Authenticating; +using FishNet.Component.Observing; +using FishNet.Connection; +using FishNet.Managing.Debugging; +using FishNet.Managing.Logging; +using FishNet.Managing.Predicting; +using FishNet.Managing.Transporting; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Transporting; +using FishNet.Utility.Extension; +using FishNet.Utility.Performance; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; + +namespace FishNet.Managing.Server +{ + /// + /// A container for server data and actions. + /// + [DisallowMultipleComponent] + [AddComponentMenu("FishNet/Manager/ServerManager")] + public sealed partial class ServerManager : MonoBehaviour + { + #region Public. + /// + /// Called after the local server connection state changes. + /// + public event Action OnServerConnectionState; + /// + /// Called when authenticator has concluded a result for a connection. Boolean is true if authentication passed, false if failed. + /// + public event Action OnAuthenticationResult; + /// + /// Called when a remote client state changes with the server. + /// + public event Action OnRemoteConnectionState; + /// + /// True if the server connection has started. + /// + public bool Started { get; private set; } + /// + /// Handling and information for objects on the server. + /// + public ServerObjects Objects { get; private set; } + /// + /// Authenticated and non-authenticated connected clients. + /// + [HideInInspector] + public Dictionary Clients = new Dictionary(); + /// + /// NetworkManager for server. + /// + [HideInInspector] + public NetworkManager NetworkManager { get; private set; } + #endregion + + #region Serialized. + /// + /// Authenticator for this ServerManager. May be null if not using authentication. + /// + [Obsolete("Use GetAuthenticator and SetAuthenticator.")] //Remove on 2023/06/01 + public Authenticator Authenticator + { + get => GetAuthenticator(); + set => SetAuthenticator(value); + } + /// + /// Gets the Authenticator for this manager. + /// + /// + public Authenticator GetAuthenticator() => _authenticator; + /// + /// Gets the Authenticator for this manager, and initializes it. + /// + /// + public void SetAuthenticator(Authenticator value) + { + _authenticator = value; + InitializeAuthenticator(); + } + [Tooltip("Authenticator for this ServerManager. May be null if not using authentication.")] + [SerializeField] + private Authenticator _authenticator; + /// + /// Default send rate for SyncTypes. A value of 0f will send changed values every tick. + /// SyncTypeRate cannot yet be changed at runtime because this would require recalculating rates on SyncBase, which is not yet implemented. + /// + /// + internal float GetSynctypeRate() => _syncTypeRate; + [Tooltip("Default send rate for SyncTypes. A value of 0f will send changed values every tick.")] + [Range(0f, 60f)] + [SerializeField] + private float _syncTypeRate = 0.1f; + /// + /// How to pack object spawns. + /// + [Tooltip("How to pack object spawns.")] + [SerializeField] + internal TransformPackingData SpawnPacking = new TransformPackingData() + { + Position = AutoPackType.Unpacked, + Rotation = AutoPackType.PackedLess, + Scale = AutoPackType.PackedLess + }; + /// + /// True to automatically set the frame rate when the client connects. + /// + [Tooltip("True to automatically set the frame rate when the client connects.")] + [SerializeField] + private bool _changeFrameRate = true; + /// + /// Maximum frame rate the server may run at. When as host this value runs at whichever is higher between client and server. + /// + internal ushort FrameRate => (_changeFrameRate) ? _frameRate : (ushort)0; + [Tooltip("Maximum frame rate the server may run at. When as host this value runs at whichever is higher between client and server.")] + [Range(1, NetworkManager.MAXIMUM_FRAMERATE)] + [SerializeField] + private ushort _frameRate = NetworkManager.MAXIMUM_FRAMERATE; + /// + /// True to share the Ids of clients and the objects they own with other clients. No sensitive information is shared. + /// + internal bool ShareIds => _shareIds; + [Tooltip("True to share the Ids of clients and the objects they own with other clients. No sensitive information is shared.")] + [SerializeField] + private bool _shareIds = true; + /// + /// Gets StartOnHeadless value. + /// + public bool GetStartOnHeadless() => _startOnHeadless; + /// + /// Sets StartOnHeadless value. + /// + /// New value to use. + public void SetStartOnHeadless(bool value) => _startOnHeadless = value; + [Tooltip("True to automatically start the server connection when running as headless.")] + [SerializeField] + private bool _startOnHeadless = true; + /// + /// True to kick clients which send data larger than the MTU. + /// + internal bool LimitClientMTU => _limitClientMTU; + [Tooltip("True to kick clients which send data larger than the MTU.")] + [SerializeField] + private bool _limitClientMTU = true; + #endregion + + #region Private. + /// + /// Used to read splits. + /// + private SplitReader _splitReader = new SplitReader(); +#if UNITY_EDITOR || DEVELOPMENT_BUILD + /// + /// Logs data about parser to help debug. + /// + private ParseLogger _parseLogger = new ParseLogger(); +#endif + #endregion + + private void OnDestroy() + { + Objects?.SubscribeToSceneLoaded(false); + } + + /// + /// Initializes this script for use. + /// + /// + internal void InitializeOnce_Internal(NetworkManager manager) + { + NetworkManager = manager; + Objects = new ServerObjects(manager); + Objects.SubscribeToSceneLoaded(true); + InitializeRpcLinks(); + //Unsubscribe first incase already subscribed. + SubscribeToTransport(false); + SubscribeToTransport(true); + NetworkManager.ClientManager.OnClientConnectionState += ClientManager_OnClientConnectionState; + NetworkManager.SceneManager.OnClientLoadedStartScenes += SceneManager_OnClientLoadedStartScenes; + + if (_authenticator == null) + _authenticator = GetComponent(); + if (_authenticator != null) + InitializeAuthenticator(); + + _cachedLevelOfDetailInterval = NetworkManager.ClientManager.LevelOfDetailInterval; + _cachedUseLod = NetworkManager.ObserverManager.GetUseNetworkLod(); + } + + /// + /// Initializes the authenticator to this manager. + /// + private void InitializeAuthenticator() + { + Authenticator auth = GetAuthenticator(); + if (auth == null || auth.Initialized) + return; + if (NetworkManager == null) + return; + + auth.InitializeOnce(NetworkManager); + auth.OnAuthenticationResult += _authenticator_OnAuthenticationResult; + } + + /// + /// Starts the server if configured to for headless. + /// + internal void StartForHeadless() + { + if (GetStartOnHeadless()) + { + //Wrapping logic in check instead of everything so _startOnHeadless doesnt warn as unused in editor. +#if UNITY_SERVER + StartConnection(); +#endif + } + } + + /// + /// Stops the local server connection. + /// + /// True to send a disconnect message to all clients first. + public bool StopConnection(bool sendDisconnectMessage) + { + if (sendDisconnectMessage) + SendDisconnectMessages(Clients.Values.ToList(), true); + + //Return stop connection result. + return NetworkManager.TransportManager.Transport.StopConnection(true); + } + + /// + /// Sends a disconnect messge to connectionIds. + /// This does not iterate outgoing automatically. + /// + /// + internal void SendDisconnectMessages(int[] connectionIds) + { + List conns = new List(); + foreach (int item in connectionIds) + { + if (Clients.TryGetValueIL2CPP(item, out NetworkConnection c)) + conns.Add(c); + } + + if (conns.Count > 0) + SendDisconnectMessages(conns, false); + } + /// + /// Sends a disconnect message to all clients and immediately iterates outgoing. + /// + private void SendDisconnectMessages(List conns, bool iterate) + { + PooledWriter writer = WriterPool.GetWriter(); + writer.WritePacketId(PacketId.Disconnect); + ArraySegment segment = writer.GetArraySegment(); + //Send segment to each client, authenticated or not. + foreach (NetworkConnection c in conns) + c.SendToClient((byte)Channel.Reliable, segment); + //Recycle writer. + writer.Dispose(); + + if (iterate) + NetworkManager.TransportManager.IterateOutgoing(true); + } + + /// + /// Starts the local server connection. + /// + public bool StartConnection() + { + return NetworkManager.TransportManager.Transport.StartConnection(true); + } + /// + /// Starts the local server using port. + /// + /// Port to start on. + /// + public bool StartConnection(ushort port) + { + Transport t = NetworkManager.TransportManager.Transport; + t.SetPort(port); + return t.StartConnection(true); + } + + /// + /// Called after the local client connection state changes. + /// + private void ClientManager_OnClientConnectionState(ClientConnectionStateArgs obj) + { + /* If client is doing anything but started destroy pending. + * Pending is only used for host mode. */ + if (obj.ConnectionState != LocalConnectionState.Started) + Objects.DestroyPending(); + } + + /// + /// Called when a client loads initial scenes after connecting. + /// + private void SceneManager_OnClientLoadedStartScenes(NetworkConnection conn, bool asServer) + { + if (asServer) + { + Objects.RebuildObservers(conn); + /* If connection is host then renderers must be hidden + * for all objects not visible to the host. The observer system + * does handle this but only after an initial state is set. + * If the clientHost joins without observation of an object + * then the initial state will never be set. */ + if (conn.IsLocalClient) + { + foreach (NetworkObject nob in Objects.Spawned.Values) + { + if (!nob.Observers.Contains(conn)) + nob.SetRenderersVisible(false); + } + } + } + } + + /// + /// Changes subscription status to transport. + /// + /// + private void SubscribeToTransport(bool subscribe) + { + if (NetworkManager == null || NetworkManager.TransportManager == null || NetworkManager.TransportManager.Transport == null) + return; + + if (subscribe) + { + NetworkManager.TransportManager.Transport.OnServerReceivedData += Transport_OnServerReceivedData; + NetworkManager.TransportManager.Transport.OnServerConnectionState += Transport_OnServerConnectionState; + NetworkManager.TransportManager.Transport.OnRemoteConnectionState += Transport_OnRemoteConnectionState; + } + else + { + NetworkManager.TransportManager.Transport.OnServerReceivedData -= Transport_OnServerReceivedData; + NetworkManager.TransportManager.Transport.OnServerConnectionState -= Transport_OnServerConnectionState; + NetworkManager.TransportManager.Transport.OnRemoteConnectionState -= Transport_OnRemoteConnectionState; + } + } + + /// + /// Called when authenticator has concluded a result for a connection. Boolean is true if authentication passed, false if failed. + /// Server listens for this event automatically. + /// + private void _authenticator_OnAuthenticationResult(NetworkConnection conn, bool authenticated) + { + if (!authenticated) + conn.Disconnect(false); + else + ClientAuthenticated(conn); + } + + /// + /// Called when a connection state changes for the local server. + /// + private void Transport_OnServerConnectionState(ServerConnectionStateArgs args) + { + /* Let the client manager know the server state is changing first. + * This gives the client an opportunity to clean-up or prepare + * before the server completes it's actions. */ + Started = AnyServerStarted(); + NetworkManager.ClientManager.Objects.OnServerConnectionState(args); + //If no servers are started then reset match conditions. + if (!Started) + { + MatchCondition.ClearMatchesWithoutRebuilding(); + //Despawn without synchronizing network objects. + Objects.DespawnWithoutSynchronization(true); + } + Objects.OnServerConnectionState(args); + + LocalConnectionState state = args.ConnectionState; + + if (NetworkManager.CanLog(LoggingType.Common)) + { + Transport t = NetworkManager.TransportManager.GetTransport(args.TransportIndex); + string tName = (t == null) ? "Unknown" : t.GetType().Name; + Debug.Log($"Local server is {state.ToString().ToLower()} for {tName}."); + } + + NetworkManager.UpdateFramerate(); + OnServerConnectionState?.Invoke(args); + } + + /// + /// Called when a connection state changes for a remote client. + /// + private void Transport_OnRemoteConnectionState(RemoteConnectionStateArgs args) + { + //Sanity check to make sure transports are following proper types/ranges. + int id = args.ConnectionId; + int maxIdValue = short.MaxValue; + if (id < 0 || id > maxIdValue) + { + Kick(args.ConnectionId, KickReason.UnexpectedProblem, LoggingType.Error, $"The transport you are using supplied an invalid connection Id of {id}. Connection Id values must range between 0 and {maxIdValue}. The client has been disconnected."); + return; + } + //Valid Id. + else + { + //If started then add to authenticated clients. + if (args.ConnectionState == RemoteConnectionState.Started) + { + NetworkManager.Log($"Remote connection started for Id {id}."); + NetworkConnection conn = new NetworkConnection(NetworkManager, id, true); + Clients.Add(args.ConnectionId, conn); + OnRemoteConnectionState?.Invoke(conn, args); + //Connection is no longer valid. This can occur if the user changes the state using the OnRemoteConnectionState event. + if (!conn.IsValid) + return; + /* If there is an authenticator + * and the transport is not a local transport. */ + Authenticator auth = GetAuthenticator(); + if (auth != null && !NetworkManager.TransportManager.IsLocalTransport(id)) + auth.OnRemoteConnection(conn); + else + ClientAuthenticated(conn); + } + //If stopping. + else if (args.ConnectionState == RemoteConnectionState.Stopped) + { + /* If client's connection is found then clean + * them up from server. */ + if (Clients.TryGetValueIL2CPP(id, out NetworkConnection conn)) + { + conn.SetDisconnecting(true); + OnRemoteConnectionState?.Invoke(conn, args); + Clients.Remove(id); + MatchCondition.RemoveFromMatchWithoutRebuild(conn, NetworkManager); + Objects.ClientDisconnected(conn); + BroadcastClientConnectionChange(false, conn); + //Return predictedObjectIds. + Queue pqId = conn.PredictedObjectIds; + while (pqId.Count > 0) + Objects.CacheObjectId(pqId.Dequeue()); + + conn.Reset(); + NetworkManager.Log($"Remote connection stopped for Id {id}."); + } + } + } + } + + /// + /// Sends client their connectionId. + /// + /// + private void SendAuthenticated(NetworkConnection conn) + { + using (PooledWriter writer = WriterPool.GetWriter()) + { + writer.WritePacketId(PacketId.Authenticated); + writer.WriteNetworkConnection(conn); + /* If predicted spawning is enabled then also send + * reserved objectIds. */ + ; + PredictionManager pm = NetworkManager.PredictionManager; + if (pm.GetAllowPredictedSpawning()) + { + int count = Mathf.Min(Objects.GetObjectIdCache().Count, pm.GetReservedObjectIds()); + writer.WriteByte((byte)count); + + for (int i = 0; i < count; i++) + { + ushort val = (ushort)Objects.GetNextNetworkObjectId(false); + writer.WriteNetworkObjectId(val); + conn.PredictedObjectIds.Enqueue(val); + } + } + + NetworkManager.TransportManager.SendToClient((byte)Channel.Reliable, writer.GetArraySegment(), conn); + } + } + /// + /// Called when the server socket receives data. + /// + private void Transport_OnServerReceivedData(ServerReceivedDataArgs args) + { + args.Data = NetworkManager.TransportManager.ProcessIntermediateIncoming(args.Data, false); + ParseReceived(args); + } + + /// + /// Called when the server receives data. + /// + /// + private void ParseReceived(ServerReceivedDataArgs args) + { +#if UNITY_EDITOR || DEVELOPMENT_BUILD + _parseLogger.Reset(); +#endif + + //Not from a valid connection. + if (args.ConnectionId < 0) + return; + ArraySegment segment = args.Data; + NetworkManager.StatisticsManager.NetworkTraffic.LocalServerReceivedData((ulong)segment.Count); + if (segment.Count <= TransportManager.TICK_BYTES) + return; + + //FishNet internally splits packets so nothing should ever arrive over MTU. + int channelMtu = NetworkManager.TransportManager.GetMTU(args.TransportIndex, (byte)args.Channel); + //If over MTU kick client immediately. + if (segment.Count > channelMtu && !NetworkManager.TransportManager.IsLocalTransport(args.ConnectionId)) + { + ExceededMTUKick(); + return; + } + + PacketId packetId = PacketId.Unset; +#if !UNITY_EDITOR && !DEVELOPMENT_BUILD + try + { +#endif + using (PooledReader reader = ReaderPool.GetReader(segment, NetworkManager)) + { + uint tick = reader.ReadUInt32(AutoPackType.Unpacked); + NetworkManager.TimeManager.LastPacketTick = tick; + /* This is a special condition where a message may arrive split. + * When this occurs buffer each packet until all packets are + * received. */ + if (reader.PeekPacketId() == PacketId.Split) + { + //Skip packetId. + reader.ReadPacketId(); + + int expectedMessages; + _splitReader.GetHeader(reader, out expectedMessages); + //If here split message can be written. + _splitReader.Write(NetworkManager.TimeManager.LastPacketTick, reader, expectedMessages); + + /* If fullMessage returns 0 count then the split + * has not written fully yet. Otherwise, if there is + * data within then reinitialize reader with the + * full message. */ + ArraySegment fullMessage = _splitReader.GetFullMessage(); + if (fullMessage.Count == 0) + return; + + /* If here then all data has been received. + * It's possible the client could have exceeded + * maximum MTU but not the maximum number of splits. + * This is because the length of each split + * is not written, so we don't know how much data of the + * final message actually belonged to the split vs + * unrelated data added afterwards. We're going to cut + * the client some slack in this situation for the sake + * of keeping things simple. */ + //Initialize reader with full message. + reader.Initialize(fullMessage, NetworkManager); + } + + //Parse reader. + while (reader.Remaining > 0) + { + packetId = reader.ReadPacketId(); +#if UNITY_EDITOR || DEVELOPMENT_BUILD + _parseLogger.AddPacket(packetId); +#endif + NetworkConnection conn; + + /* Connection isn't available. This should never happen. + * Force an immediate disconnect. */ + if (!Clients.TryGetValueIL2CPP(args.ConnectionId, out conn)) + { + Kick(args.ConnectionId, KickReason.UnexpectedProblem, LoggingType.Error, $"ConnectionId {conn.ClientId} not found within Clients. Connection will be kicked immediately."); + return; + } + conn.SetLastPacketTick(tick); + /* If connection isn't authenticated and isn't a broadcast + * then disconnect client. If a broadcast then process + * normally; client may still become disconnected if the broadcast + * does not allow to be called while not authenticated. */ + if (!conn.Authenticated && packetId != PacketId.Broadcast) + { + conn.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"ConnectionId {conn.ClientId} sent a Broadcast without being authenticated. Connection will be kicked immediately."); + return; + } + + //Only check if not developer build because users pay pause editor. +#if !DEVELOPMENT_BUILD && !UNITY_EDITOR + /* If hasn't sent LOD recently enough. LODs are sent every half a second, so + * by multiplaying interval by 60 this gives the client a 30 second window. */ + if (_cachedUseLod && conn.IsLateForLevelOfDetail(_cachedLevelOfDetailInterval * 60)) + { + conn.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"ConnectionId {conn.ClientId} has gone too long without sending a level of detail update. Connection will be kicked immediately."); + return; + } +#endif + if (packetId == PacketId.Replicate) + { + Objects.ParseReplicateRpc(reader, conn, args.Channel); + } + else if (packetId == PacketId.ServerRpc) + { + Objects.ParseServerRpc(reader, conn, args.Channel); + } + else if (packetId == PacketId.ObjectSpawn) + { + if (!NetworkManager.PredictionManager.GetAllowPredictedSpawning()) + { + conn.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"ConnectionId {conn.ClientId} sent a predicted spawn while predicted spawning is not enabled. Connection will be kicked immediately."); + return; + } + Objects.ReadPredictedSpawn(reader, conn); + } + else if (packetId == PacketId.ObjectDespawn) + { + if (!NetworkManager.PredictionManager.GetAllowPredictedSpawning()) + { + conn.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"ConnectionId {conn.ClientId} sent a predicted spawn while predicted spawning is not enabled. Connection will be kicked immediately."); + return; + } + Objects.ReadPredictedDespawn(reader, conn); + } + else if (packetId == PacketId.NetworkLODUpdate) + { + ParseNetworkLODUpdate(reader, conn); + } + else if (packetId == PacketId.Broadcast) + { + ParseBroadcast(reader, conn, args.Channel); + } + else if (packetId == PacketId.PingPong) + { + ParsePingPong(reader, conn); + } + else + { +#if UNITY_EDITOR || DEVELOPMENT_BUILD + NetworkManager.LogError($"Server received an unhandled PacketId of {(ushort)packetId} from connectionId {args.ConnectionId}. Remaining data has been purged."); + _parseLogger.Print(NetworkManager); +#else + NetworkManager.LogError($"Server received an unhandled PacketId of {(ushort)packetId} from connectionId {args.ConnectionId}. Connection will be kicked immediately."); + NetworkManager.TransportManager.Transport.StopConnection(args.ConnectionId, true); +#endif + return; + } + } + } +#if !UNITY_EDITOR && !DEVELOPMENT_BUILD + } + catch (Exception e) + { + Kick(args.ConnectionId, KickReason.MalformedData, LoggingType.Error, $"Server encountered an error while parsing data for packetId {packetId} from connectionId {args.ConnectionId}. Connection will be kicked immediately. Message: {e.Message}."); + } +#endif + + //Kicks connection for exceeding MTU. + void ExceededMTUKick() + { + Kick(args.ConnectionId, KickReason.ExploitExcessiveData, LoggingType.Common, $"ConnectionId {args.ConnectionId} sent a message larger than allowed amount. Connection will be kicked immediately."); + } + + } + + /// + /// Parses a received PingPong. + /// + /// + /// + private void ParsePingPong(PooledReader reader, NetworkConnection conn) + { + /* //security limit how often clients can send pings. + * have clients use a stopwatch rather than frame time + * for checks to ensure it's not possible to send + * excessively should their game stutter then catch back up. */ + uint clientTick = reader.ReadUInt32(AutoPackType.Unpacked); + if (conn.CanPingPong()) + NetworkManager.TimeManager.SendPong(conn, clientTick); + } + + + /// + /// Called when a remote client authenticates with the server. + /// + /// + private void ClientAuthenticated(NetworkConnection connection) + { + /* Immediately send connectionId to client. Some transports + * don't give clients their remoteId, therefor it has to be sent + * by the ServerManager. This packet is very simple and can be built + * on the spot. */ + connection.ConnectionAuthenticated(); + /* Send client Ids before telling the client + * they are authenticated. This is important because when the client becomes + * authenticated they set their LocalConnection using Clients field in ClientManager, + * which is set after getting Ids. */ + BroadcastClientConnectionChange(true, connection); + SendAuthenticated(connection); + + OnAuthenticationResult?.Invoke(connection, true); + NetworkManager.SceneManager.OnClientAuthenticated(connection); + } + + /// + /// Sends a client connection state change to owner and other clients if applicable. + /// + private void BroadcastClientConnectionChange(bool connected, NetworkConnection conn) + { + //If sharing Ids then send all connected client Ids first if is a connected state. + if (ShareIds) + { + /* Send a broadcast to all authenticated clients with the clientId + * that just connected. The conn client will also get this. */ + ClientConnectionChangeBroadcast changeMsg = new ClientConnectionChangeBroadcast() + { + Connected = connected, + Id = conn.ClientId + }; + Broadcast(changeMsg); + + /* If state is connected then the conn client + * must also receive all currently connected client ids. */ + if (connected) + { + //Send already connected clients to the connection that just joined. + ListCache lc = ListCaches.GetIntCache(); + foreach (int key in Clients.Keys) + lc.AddValue(key); + + ConnectedClientsBroadcast allMsg = new ConnectedClientsBroadcast() + { + ListCache = lc + }; + conn.Broadcast(allMsg); + ListCaches.StoreCache(lc); + } + } + //If not sharing Ids then only send ConnectionChange to conn. + else + { + if (connected) + { + /* Send broadcast only to the client which just disconnected. + * Only send if connecting. If the client is disconnected there's no reason + * to send them a disconnect msg. */ + ClientConnectionChangeBroadcast changeMsg = new ClientConnectionChangeBroadcast() + { + Connected = connected, + Id = conn.ClientId + }; + Broadcast(conn, changeMsg, true, Channel.Reliable); + } + } + + } + + } + + +} diff --git a/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs.meta b/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs.meta new file mode 100644 index 0000000..9a70fbe --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 68828c85278210948b9d50a8db3aab74 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Statistic.meta b/Assets/FishNet/Runtime/Managing/Statistic.meta new file mode 100644 index 0000000..5fdb9ab --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Statistic.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c554690fa24f652408df67e97c3a0e5f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficArgs.cs b/Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficArgs.cs new file mode 100644 index 0000000..9f9a5f2 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficArgs.cs @@ -0,0 +1,21 @@ +namespace FishNet.Managing.Statistic +{ + + public struct NetworkTrafficArgs + { + /// + /// Number of bytes sent to the server. + /// + public readonly ulong ToServerBytes; + /// + /// Number of bytes sent by the server. + /// + public readonly ulong FromServerBytes; + + public NetworkTrafficArgs(ulong toServerBytes, ulong fromServerBytes) + { + ToServerBytes = toServerBytes; + FromServerBytes = fromServerBytes; + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficArgs.cs.meta b/Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficArgs.cs.meta new file mode 100644 index 0000000..ff42599 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficArgs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1fb953771006e0541ba76e564a90c21d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficStatistics.cs b/Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficStatistics.cs new file mode 100644 index 0000000..f50b1c7 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficStatistics.cs @@ -0,0 +1,203 @@ + + +using System; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Managing.Statistic +{ + [System.Serializable] + public class NetworkTraficStatistics + { + #region Public. + /// + /// Called when NetworkTraffic is updated for the client. + /// + public event Action OnClientNetworkTraffic; + /// + /// Called when NetworkTraffic is updated for the server. + /// + public event Action OnServerNetworkTraffic; + #endregion + + #region Serialized. + /// + /// How often to update traffic statistics. + /// + [Tooltip("How often to update traffic statistics.")] + [SerializeField] + [Range(0f, 10f)] + private float _updateInteval = 1f; + /// + /// + /// + [Tooltip("True to update client statistics.")] + [SerializeField] + private bool _updateClient; + /// + /// True to update client statistics. + /// + public bool UpdateClient + { + get => _updateClient; + private set => _updateClient = value; + } + /// + /// Sets UpdateClient value. + /// + /// + public void SetUpdateClient(bool update) + { + UpdateClient = update; + } + /// + /// + /// + [Tooltip("True to update server statistics.")] + [SerializeField] + private bool _updateServer; + /// + /// True to update client statistics. + /// + public bool UpdateServer + { + get => _updateServer; + private set => _updateServer = value; + } + /// + /// Sets UpdateServer value. + /// + /// + public void SetUpdateServer(bool update) + { + UpdateServer = update; + } + #endregion + + #region Private. + /// + /// NetworkManager for this statistics. + /// + private NetworkManager _networkManager; + /// + /// Bytes sent to the server from local client. + /// + private ulong _client_toServerBytes; + /// + /// Bytes received on the local client from the server. + /// + private ulong _client_fromServerBytes; + /// + /// Bytes sent to all clients from the local server. + /// + private ulong _server_toClientsBytes; + /// + /// Bytes received on the local server from all clients. + /// + private ulong _server_fromClientsBytes; + /// + /// Next time network traffic updates may invoke. + /// + private float _nextUpdateTime; + /// + /// Size suffixes as text. + /// + private static readonly string[] _sizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; + #endregion + + internal void InitializeOnce_Internal(NetworkManager manager) + { + _networkManager = manager; + manager.TimeManager.OnPreTick += TimeManager_OnPreTick; + } + + /// + /// Called before the TimeManager ticks. + /// + private void TimeManager_OnPreTick() + { + if (Time.unscaledTime < _nextUpdateTime) + return; + _nextUpdateTime = Time.unscaledTime + _updateInteval; + + if (UpdateClient && _networkManager.IsClient) + OnClientNetworkTraffic?.Invoke(new NetworkTrafficArgs(_client_toServerBytes, _client_fromServerBytes)); + if (UpdateServer && _networkManager.IsServer) + OnServerNetworkTraffic?.Invoke(new NetworkTrafficArgs(_server_fromClientsBytes, _server_toClientsBytes)); + + _client_toServerBytes = 0; + _client_fromServerBytes = 0; + _server_toClientsBytes = 0; + _server_fromClientsBytes = 0; + } + + /// + /// Called when the local client sends data. + /// + internal void LocalClientSentData(ulong dataLength) + { + _client_toServerBytes = Math.Min(_client_toServerBytes + dataLength, ulong.MaxValue); + } + /// + /// Called when the local client receives data. + /// + public void LocalClientReceivedData(ulong dataLength) + { + _client_fromServerBytes = Math.Min(_client_fromServerBytes + dataLength, ulong.MaxValue); + } + + + /// + /// Called when the local client sends data. + /// + internal void LocalServerSentData(ulong dataLength) + { + _server_toClientsBytes = Math.Min(_server_toClientsBytes + dataLength, ulong.MaxValue); + } + /// + /// Called when the local client receives data. + /// + public void LocalServerReceivedData(ulong dataLength) + { + _server_fromClientsBytes = Math.Min(_server_fromClientsBytes + dataLength, ulong.MaxValue); + } + + + //Attribution: https://stackoverflow.com/questions/14488796/does-net-provide-an-easy-way-convert-bytes-to-kb-mb-gb-etc + /// + /// Formats passed in bytes value to the largest possible data type with 2 decimals. + /// + public static string FormatBytesToLargest(ulong bytes) + { + int decimalPlaces = 2; + if (bytes == 0) + { + decimalPlaces = 0; + return string.Format("{0:n" + decimalPlaces + "} bytes", 0); + } + + // mag is 0 for bytes, 1 for KB, 2, for MB, etc. + int mag = (int)Math.Log(bytes, 1024); + + // 1L << (mag * 10) == 2 ^ (10 * mag) + // [i.e. the number of bytes in the unit corresponding to mag] + decimal adjustedSize = (decimal)bytes / (1L << (mag * 10)); + + // make adjustment when the value is large enough that + // it would round up to 1000 or more + if (Math.Round(adjustedSize, decimalPlaces) >= 1000) + { + mag += 1; + adjustedSize /= 1024; + } + + //Don't show decimals for bytes. + if (mag == 0) + decimalPlaces = 0; + + return string.Format("{0:n" + decimalPlaces + "} {1}", adjustedSize, _sizeSuffixes[mag]); + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficStatistics.cs.meta b/Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficStatistics.cs.meta new file mode 100644 index 0000000..77c4fbc --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficStatistics.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9cc04f6ae0339c94e9153396dce3f46e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Statistic/StatisticsManager.cs b/Assets/FishNet/Runtime/Managing/Statistic/StatisticsManager.cs new file mode 100644 index 0000000..3570745 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Statistic/StatisticsManager.cs @@ -0,0 +1,22 @@ +using UnityEngine; + +namespace FishNet.Managing.Statistic +{ + + [DisallowMultipleComponent] + [AddComponentMenu("FishNet/Manager/StatisticsManager")] + public class StatisticsManager : MonoBehaviour + { + /// + /// Statistics for NetworkTraffic. + /// + public NetworkTraficStatistics NetworkTraffic = new NetworkTraficStatistics(); + + internal void InitializeOnce_Internal(NetworkManager manager) + { + NetworkTraffic.InitializeOnce_Internal(manager); + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Statistic/StatisticsManager.cs.meta b/Assets/FishNet/Runtime/Managing/Statistic/StatisticsManager.cs.meta new file mode 100644 index 0000000..f50fb2f --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Statistic/StatisticsManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 756c28cd3141c4140ae776188ee26729 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Timing.meta b/Assets/FishNet/Runtime/Managing/Timing.meta new file mode 100644 index 0000000..35f0ef3 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: edf228892c89f3d4694213cba4a584fd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Timing/Editor.meta b/Assets/FishNet/Runtime/Managing/Timing/Editor.meta new file mode 100644 index 0000000..f5e62ae --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 142c456200062324b95ea95078a9c38f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Timing/Editor/TimeManagerEditor.cs b/Assets/FishNet/Runtime/Managing/Timing/Editor/TimeManagerEditor.cs new file mode 100644 index 0000000..fafb77c --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/Editor/TimeManagerEditor.cs @@ -0,0 +1,79 @@ +#if UNITY_EDITOR +using UnityEditor; +using UnityEngine; + +namespace FishNet.Managing.Timing.Editing +{ + + + [CustomEditor(typeof(TimeManager), true)] + [CanEditMultipleObjects] + public class TimeManagerEditor : Editor + { + private SerializedProperty _updateOrder; + private SerializedProperty _timingType; + private SerializedProperty _tickRate; + private SerializedProperty _allowTickDropping; + private SerializedProperty _maximumFrameTicks; + private SerializedProperty _pingInterval; + private SerializedProperty _timingInterval; + private SerializedProperty _physicsMode; + + protected virtual void OnEnable() + { + _updateOrder = serializedObject.FindProperty("_updateOrder"); + _timingType = serializedObject.FindProperty("_timingType"); + _tickRate = serializedObject.FindProperty("_tickRate"); + _allowTickDropping = serializedObject.FindProperty("_allowTickDropping"); + _maximumFrameTicks = serializedObject.FindProperty("_maximumFrameTicks"); + _pingInterval = serializedObject.FindProperty("_pingInterval"); + _timingInterval = serializedObject.FindProperty("_timingInterval"); + _physicsMode = serializedObject.FindProperty("_physicsMode"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + GUI.enabled = false; + EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour((TimeManager)target), typeof(TimeManager), false); + GUI.enabled = true; + + //Timing. + EditorGUILayout.LabelField("Timing", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_updateOrder); + EditorGUILayout.PropertyField(_timingType); + EditorGUILayout.PropertyField(_allowTickDropping); + if (_allowTickDropping.boolValue == true) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_maximumFrameTicks); + EditorGUI.indentLevel--; + } + EditorGUILayout.PropertyField(_tickRate); + EditorGUILayout.PropertyField(_pingInterval); + EditorGUILayout.PropertyField(_timingInterval); + EditorGUI.indentLevel--; + EditorGUILayout.Space(); + + //Physics. + EditorGUILayout.LabelField("Physics", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_physicsMode); + if (_physicsMode.intValue != (int)FishNet.Managing.Timing.PhysicsMode.TimeManager) + EditorGUILayout.HelpBox("If you are using physics interactions be sure to change the PhysicsMode to TimeManager and implement physics within the TimeManager tick events.", MessageType.None); + EditorGUI.indentLevel--; + + ////Prediction. + //EditorGUILayout.LabelField("Prediction", EditorStyles.boldLabel); + //EditorGUI.indentLevel++; + //EditorGUILayout.PropertyField(_maximumBufferedInputs); + //EditorGUI.indentLevel--; + + serializedObject.ApplyModifiedProperties(); + } + } + +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Timing/Editor/TimeManagerEditor.cs.meta b/Assets/FishNet/Runtime/Managing/Timing/Editor/TimeManagerEditor.cs.meta new file mode 100644 index 0000000..cb15067 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/Editor/TimeManagerEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 12987a8c0302190489ecb55f6fbd494e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Timing/MovingAverage.cs b/Assets/FishNet/Runtime/Managing/Timing/MovingAverage.cs new file mode 100644 index 0000000..a410716 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/MovingAverage.cs @@ -0,0 +1,81 @@ +using FishNet.Documenting; +using System; +using UnityEngine; + +namespace FishNet.Managing.Timing +{ + + [APIExclude] + public class MovingAverage + { + #region Public. + /// + /// Average from samples favoring the most recent sample. + /// + public float Average { get; private set; } + #endregion + + /// + /// Next index to write a sample to. + /// + private int _writeIndex; + /// + /// Collected samples. + /// + private float[] _samples; + /// + /// Number of samples written. Will be at most samples size. + /// + private int _writtenSamples; + /// + /// Samples accumulated over queue. + /// + private float _sampleAccumulator; + + public MovingAverage(int sampleSize) + { + if (sampleSize < 0) + sampleSize = 0; + else if (sampleSize < 2) + NetworkManager.StaticLogWarning("Using a sampleSize of less than 2 will always return the most recent value as Average."); + + _samples = new float[sampleSize]; + } + + + /// + /// Computes a new windowed average each time a new sample arrives + /// + /// + public void ComputeAverage(float newSample) + { + if (_samples.Length <= 1) + { + Average = newSample; + return; + } + + _sampleAccumulator += newSample; + _samples[_writeIndex] = newSample; + + //Increase writeIndex. + _writeIndex++; + _writtenSamples = Math.Max(_writtenSamples, _writeIndex); + if (_writeIndex >= _samples.Length) + _writeIndex = 0; + + Average = _sampleAccumulator / _writtenSamples; + + /* If samples are full then drop off + * the oldest sample. This will always be + * the one just after written. The entry isn't + * actually removed from the array but will + * be overwritten next sample. */ + if (_writtenSamples >= _samples.Length) + _sampleAccumulator -= _samples[_writeIndex]; + + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Timing/MovingAverage.cs.meta b/Assets/FishNet/Runtime/Managing/Timing/MovingAverage.cs.meta new file mode 100644 index 0000000..941fad9 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/MovingAverage.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 03d05f88778c5c744810e48f251f2d3b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Timing/PhysicsMode.cs b/Assets/FishNet/Runtime/Managing/Timing/PhysicsMode.cs new file mode 100644 index 0000000..6a77670 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/PhysicsMode.cs @@ -0,0 +1,23 @@ +namespace FishNet.Managing.Timing +{ + /// + /// How to simulate physics. + /// + public enum PhysicsMode + { + /// + /// Unity performs physics every FixedUpdate. + /// + Unity = 0, + /// + /// TimeManager performs physics each tick. + /// + TimeManager = 1, + /// + /// Physics will be disabled. + /// + Disabled = 2 + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Timing/PhysicsMode.cs.meta b/Assets/FishNet/Runtime/Managing/Timing/PhysicsMode.cs.meta new file mode 100644 index 0000000..6b21845 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/PhysicsMode.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 502e9f31bebd41f4f9088a19eae53735 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Timing/PreciseTick.cs b/Assets/FishNet/Runtime/Managing/Timing/PreciseTick.cs new file mode 100644 index 0000000..f940a70 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/PreciseTick.cs @@ -0,0 +1,47 @@ +using FishNet.Serializing; +using FishNet.Utility.Extension; +using System; +using UnityEngine; + +namespace FishNet.Managing.Timing +{ + + public struct PreciseTick + { + /// + /// The current tick. + /// + public uint Tick; + /// + /// Percentage into the next tick. + /// + public double Percent; + + public PreciseTick(uint tick, double percent) + { + Tick = tick; + Percent = percent; + } + } + + public static class PreciseTickSerializer + { + public static void WritePreciseTick(this Writer writer, PreciseTick value) + { + writer.WriteUInt32(value.Tick, AutoPackType.Unpacked); + /* No reason percent should exist beyond these values, but better to be safe. + * There is also no double clamp in Unity so... */ + double percent = MathFN.ClampDouble(value.Percent, 0d, 1f); + byte percentByte = (byte)(percent * 100); + writer.WriteByte(percentByte); + } + + public static PreciseTick ReadPreciseTick(this Reader reader) + { + uint tick = reader.ReadUInt32(AutoPackType.Unpacked); + byte percentByte = reader.ReadByte(); + double percent = MathFN.ClampDouble((percentByte / 100f), 0d, 1d); + return new PreciseTick(tick, percent); + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Timing/PreciseTick.cs.meta b/Assets/FishNet/Runtime/Managing/Timing/PreciseTick.cs.meta new file mode 100644 index 0000000..4d9406a --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/PreciseTick.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a96dd6b21066a424199583b80746464f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Timing/TickRounding.cs b/Assets/FishNet/Runtime/Managing/Timing/TickRounding.cs new file mode 100644 index 0000000..a3800d3 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/TickRounding.cs @@ -0,0 +1,23 @@ +namespace FishNet.Managing.Timing +{ + /// + /// How ticks are rounded when using time. + /// + public enum TickRounding + { + /// + /// Rounds up. + /// + RoundUp, + /// + /// Rounds down. + /// + RoundDown, + /// + /// Rounds to the nearest whole. + /// + RoundNearest + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Timing/TickRounding.cs.meta b/Assets/FishNet/Runtime/Managing/Timing/TickRounding.cs.meta new file mode 100644 index 0000000..02d1339 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/TickRounding.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d12f8894fc7343b4bbe332464dc4bce5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Timing/TickType.cs b/Assets/FishNet/Runtime/Managing/Timing/TickType.cs new file mode 100644 index 0000000..0726324 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/TickType.cs @@ -0,0 +1,10 @@ +namespace FishNet.Managing.Timing +{ + public enum TickType : byte + { + Tick = 0, + LocalTick = 1, + LastPacketTick = 2 + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Timing/TickType.cs.meta b/Assets/FishNet/Runtime/Managing/Timing/TickType.cs.meta new file mode 100644 index 0000000..cebcd6e --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/TickType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 489fba8b0da3c9b4b9ff4e7a46804473 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs b/Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs new file mode 100644 index 0000000..8624e31 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs @@ -0,0 +1,1054 @@ +using FishNet.Connection; +using FishNet.Documenting; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Serializing.Helping; +using FishNet.Transporting; +using FishNet.Utility; +using FishNet.Utility.Extension; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; +using SystemStopwatch = System.Diagnostics.Stopwatch; +using UnityScene = UnityEngine.SceneManagement.Scene; + +namespace FishNet.Managing.Timing +{ + + /// + /// Provides data and actions for network time and tick based systems. + /// + [DisallowMultipleComponent] + [AddComponentMenu("FishNet/Manager/TimeManager")] + public sealed partial class TimeManager : MonoBehaviour + { + #region Types. + /// + /// How networking timing is performed. + /// + private enum TimingType + { + /// + /// Send and read data on tick. + /// + Tick = 0, + /// + /// Send and read data as soon as possible. This does not include built-in components, which will still run on tick. + /// + Variable = 1 + } + private enum UpdateOrder : byte + { + BeforeTick = 0, + AfterTick = 1, + } + #endregion + + #region Public. + /// + /// Called when the local clients ping is updated. + /// + public event Action OnRoundTripTimeUpdated; + /// + /// Called right before a tick occurs, as well before data is read. + /// + public event Action OnPreTick; + /// + /// Called when a tick occurs. + /// + public event Action OnTick; + /// + /// When using TimeManager for physics timing, this is called immediately before physics simulation will occur for the tick. + /// While using Unity for physics timing, this is called during FixedUpdate. + /// This may be useful if you wish to run physics differently for stacked scenes. + /// + public event Action OnPrePhysicsSimulation; + /// + /// When using TimeManager for physics timing, this is called immediately after the physics simulation has occured for the tick. + /// While using Unity for physics timing, this is called during Update, only if a physics frame. + /// This may be useful if you wish to run physics differently for stacked scenes. + /// + public event Action OnPostPhysicsSimulation; + /// + /// Called after a tick occurs; physics would have simulated if using PhysicsMode.TimeManager. + /// + public event Action OnPostTick; + /// + /// Called when MonoBehaviours call Update. + /// + public event Action OnUpdate; + /// + /// Called when MonoBehaviours call LateUpdate. + /// + public event Action OnLateUpdate; + /// + /// Called when MonoBehaviours call FixedUpdate. + /// + public event Action OnFixedUpdate; + /// + /// RoundTripTime in milliseconds. This value includes latency from the tick rate. + /// + public long RoundTripTime { get; private set; } + /// + /// True if the number of frames per second are less than the number of expected ticks per second. + /// + internal bool LowFrameRate => ((Time.unscaledTime - _lastMultipleTicksTime) < 1f); + /// + /// Tick on the last received packet, be it from server or client. + /// + public uint LastPacketTick { get; internal set; } + /// + /// Current approximate network tick as it is on server. + /// When running as client only this is an approximation to what the server tick is. + /// The value of this field may increase and decrease as timing adjusts. + /// This value is reset upon disconnecting. + /// Tick can be used to get the server time by using TicksToTime(). + /// Use LocalTick for values that only increase. + /// + public uint Tick { get; internal set; } + /// + /// A fixed deltaTime for TickRate. + /// + [HideInInspector] + public double TickDelta { get; private set; } + /// + /// True if the TimeManager will or has ticked this frame. + /// + public bool FrameTicked { get; private set; } + /// + /// How long the local server has been connected. + /// + public float ServerUptime { get; private set; } + /// + /// How long the local client has been connected. + /// + public float ClientUptime { get; private set; } + #endregion + + #region Serialized. + /// + /// When to invoke OnUpdate and other Unity callbacks relayed by the TimeManager. + /// + [Tooltip("When to invoke OnUpdate and other Unity callbacks relayed by the TimeManager.")] + [SerializeField] + private UpdateOrder _updateOrder = UpdateOrder.BeforeTick; + /// + /// Timing for sending and receiving data. + /// + [Tooltip("Timing for sending and receiving data.")] + [SerializeField] + private TimingType _timingType = TimingType.Tick; + /// + /// While true clients may drop local ticks if their devices are unable to maintain the tick rate. + /// This could result in a temporary desynchronization but will prevent the client falling further behind on ticks by repeatedly running the logic cycle multiple times per frame. + /// + [Tooltip("While true clients may drop local ticks if their devices are unable to maintain the tick rate. This could result in a temporary desynchronization but will prevent the client falling further behind on ticks by repeatedly running the logic cycle multiple times per frame.")] + [SerializeField] + private bool _allowTickDropping; + /// + /// Maximum number of ticks which may occur in a single frame before remainder are dropped for the frame. + /// + [Tooltip("Maximum number of ticks which may occur in a single frame before remainder are dropped for the frame.")] + [Range(1, 25)] + [SerializeField] + private byte _maximumFrameTicks = 2; + /// + /// + /// + [Tooltip("How many times per second the server will simulate. This does not limit server frame rate.")] + [Range(1, 240)] + [SerializeField] + private ushort _tickRate = 30; + /// + /// How many times per second the server will simulate. This does not limit server frame rate. + /// + public ushort TickRate { get => _tickRate; private set => _tickRate = value; } + /// + /// + /// + [Tooltip("How often in seconds to a connections ping. This is also responsible for approximating server tick. This value does not affect prediction.")] + [Range(1, 15)] + [SerializeField] + private byte _pingInterval = 1; + /// + /// How often in seconds to a connections ping. This is also responsible for approximating server tick. This value does not affect prediction. + /// + internal byte PingInterval => _pingInterval; + /// + /// How often in seconds to update prediction timing. Lower values will result in marginally more accurate timings at the cost of bandwidth. + /// + [Tooltip("How often in seconds to update prediction timing. Lower values will result in marginally more accurate timings at the cost of bandwidth.")] + [Range(1, 15)] + [SerializeField] + private byte _timingInterval = 2; + /// + /// + /// + [Tooltip("How to perform physics.")] + [SerializeField] + private PhysicsMode _physicsMode = PhysicsMode.Unity; + /// + /// How to perform physics. + /// + public PhysicsMode PhysicsMode => _physicsMode; + #endregion + + #region Private. + /// + /// Ticks that have passed on client since the last time server sent an UpdateTicksBroadcast. + /// + private uint _clientTicks = 0; + /// + /// Last Tick the server sent out UpdateTicksBroadcast. + /// + private uint _lastUpdateTicks = 0; + /// + /// + /// + private uint _localTick; + /// + /// A tick that is not synchronized. This value will only increment. May be used for indexing or Ids with custom logic. + /// When called on the server Tick is returned, otherwise LocalTick is returned. + /// This value resets upon disconnecting. + /// + public uint LocalTick + { + get => (_networkManager.IsServer) ? Tick : _localTick; + private set => _localTick = value; + } + /// + /// Stopwatch used for pings. + /// + SystemStopwatch _pingStopwatch = new SystemStopwatch(); + /// + /// Ticks passed since last ping. + /// + private uint _pingTicks; + /// + /// MovingAverage instance used to calculate mean ping. + /// + private MovingAverage _pingAverage = new MovingAverage(5); + /// + /// Accumulating frame time to determine when to increase tick. + /// + private double _elapsedTickTime; + /// + /// NetworkManager used with this. + /// + private NetworkManager _networkManager; + /// + /// Internal deltaTime for clients. Controlled by the server. + /// + private double _adjustedTickDelta; + /// + /// Range which client timing may reside within. + /// + private double[] _clientTimingRange; + /// + /// Last frame an iteration occurred for incoming. + /// + private int _lastIncomingIterationFrame = -1; + /// + /// True if client received Pong since last ping. + /// + private bool _receivedPong = true; + /// + /// Last unscaledTime multiple ticks occurred in a single frame. + /// + private float _lastMultipleTicksTime; + /// + /// Number of TimeManagers open which are using manual physics. + /// + private static uint _manualPhysics; + #endregion + + #region Const. + /// + /// Maximum percentage timing may vary from SimulationInterval for clients. + /// + private const float CLIENT_TIMING_PERCENT_RANGE = 0.5f; + /// + /// Percentage of TickDelta client will adjust when needing to speed up. + /// + private const double CLIENT_SPEEDUP_PERCENT = 0.003d; + /// + /// Percentage of TickDelta client will adjust when needing to slow down. + /// + private const double CLIENT_SLOWDOWN_PERCENT = 0.005d; + /// + /// When steps to be sent to clients are equal to or higher than this value in either direction a reset steps will be sent. + /// + private const byte RESET_STEPS_THRESHOLD = 5; + /// + /// Playerprefs string to load and save user fixed time. + /// + private const string SAVED_FIXED_TIME_TEXT = "SavedFixedTimeFN"; + #endregion + +#if UNITY_EDITOR + private void OnDisable() + { + //If closing/stopping. + if (ApplicationState.IsQuitting()) + { + _manualPhysics = 0; + UnsetSimulationSettings(); + } + else if (PhysicsMode == PhysicsMode.TimeManager) + { + _manualPhysics = Math.Max(0, _manualPhysics - 1); + } + } +#endif + + /// + /// Called when FixedUpdate ticks. This is called before any other script. + /// + internal void TickFixedUpdate() + { + OnFixedUpdate?.Invoke(); + /* Invoke onsimulation if using Unity time. + * Otherwise let the tick cycling part invoke. */ + if (PhysicsMode == PhysicsMode.Unity) + OnPrePhysicsSimulation?.Invoke(Time.fixedDeltaTime); + } + + /// + /// Called when Update ticks. This is called before any other script. + /// + internal void TickUpdate() + { + if (_networkManager.IsServer) + ServerUptime += Time.deltaTime; + if (_networkManager.IsClient) + ClientUptime += Time.deltaTime; + + bool beforeTick = (_updateOrder == UpdateOrder.BeforeTick); + if (beforeTick) + { + OnUpdate?.Invoke(); + MethodLogic(); + } + else + { + MethodLogic(); + OnUpdate?.Invoke(); + } + + void MethodLogic() + { + IncreaseTick(); + /* Invoke onsimulation if using Unity time. + * Otherwise let the tick cycling part invoke. */ + if (PhysicsMode == PhysicsMode.Unity && Time.inFixedTimeStep) + OnPostPhysicsSimulation?.Invoke(Time.fixedDeltaTime); + } + } + + /// + /// Called when LateUpdate ticks. This is called after all other scripts. + /// + internal void TickLateUpdate() + { + OnLateUpdate?.Invoke(); + } + + + /// + /// Initializes this script for use. + /// + internal void InitializeOnce_Internal(NetworkManager networkManager) + { + _networkManager = networkManager; + SetInitialValues(); + _networkManager.ServerManager.OnServerConnectionState += ServerManager_OnServerConnectionState; + _networkManager.ClientManager.OnClientConnectionState += ClientManager_OnClientConnectionState; + + AddNetworkLoops(); + } + + /// + /// Adds network loops to gameObject. + /// + private void AddNetworkLoops() + { + //Writer. + if (!gameObject.TryGetComponent(out _)) + gameObject.AddComponent(); + //Reader. + if (!gameObject.TryGetComponent(out _)) + gameObject.AddComponent(); + } + + + /// + /// Called after the local client connection state changes. + /// + private void ClientManager_OnClientConnectionState(ClientConnectionStateArgs obj) + { + if (obj.ConnectionState != LocalConnectionState.Started) + { + _pingStopwatch.Stop(); + ClientUptime = 0f; + + //Only reset ticks if also not server. + if (!_networkManager.IsServer) + { + LocalTick = 0; + Tick = 0; + } + } + //Started. + else + { + _pingStopwatch.Restart(); + } + } + + /// + /// Called after the local server connection state changes. + /// + private void ServerManager_OnServerConnectionState(ServerConnectionStateArgs obj) + { + //If no servers are running. + if (!_networkManager.ServerManager.AnyServerStarted()) + { + ServerUptime = 0f; + Tick = 0; + } + } + + /// + /// Sets values to use based on settings. + /// + private void SetInitialValues() + { + SetTickRate(TickRate); + InitializePhysicsMode(PhysicsMode); + } + + /// + /// Sets simulation settings to Unity defaults. + /// + private void UnsetSimulationSettings() + { + Physics.autoSimulation = true; +#if !UNITY_2020_2_OR_NEWER + Physics2D.autoSimulation = true; +#else + Physics2D.simulationMode = SimulationMode2D.FixedUpdate; +#endif + + float simulationTime = PlayerPrefs.GetFloat(SAVED_FIXED_TIME_TEXT, float.MinValue); + if (simulationTime != float.MinValue) + Time.fixedDeltaTime = simulationTime; + } + + /// + /// Initializes physics mode when starting. + /// + /// + private void InitializePhysicsMode(PhysicsMode mode) + { + //Disable. + if (mode == PhysicsMode.Disabled) + { + SetPhysicsMode(mode); + } + //Do not automatically simulate. + else if (mode == PhysicsMode.TimeManager) + { +#if UNITY_EDITOR + //Preserve user tick rate. + PlayerPrefs.SetFloat(SAVED_FIXED_TIME_TEXT, Time.fixedDeltaTime); + //Let the player know. + if (Time.fixedDeltaTime != (float)TickDelta) + Debug.LogWarning("Time.fixedDeltaTime is being overriden with TimeManager.TickDelta"); +#endif + Time.fixedDeltaTime = (float)TickDelta; + /* Only check this if network manager + * is not null. It would be null via + * OnValidate. */ + if (_networkManager != null) + { + //If at least one time manager is already running manual physics. + if (_manualPhysics > 0) + _networkManager.LogError($"There are multiple TimeManagers instantiated which are using manual physics. Manual physics with multiple TimeManagers is not supported."); + + _manualPhysics++; + } + + SetPhysicsMode(mode); + } + //Automatically simulate. + else + { +#if UNITY_EDITOR + float savedTime = PlayerPrefs.GetFloat(SAVED_FIXED_TIME_TEXT, float.MinValue); + if (savedTime != float.MinValue && Time.fixedDeltaTime != savedTime) + { + Debug.LogWarning("Time.fixedDeltaTime has been set back to user values."); + Time.fixedDeltaTime = savedTime; + } + + PlayerPrefs.DeleteKey(SAVED_FIXED_TIME_TEXT); +#endif + SetPhysicsMode(mode); + } + } + + /// + /// Updates physics based on which physics mode to use. + /// + /// + public void SetPhysicsMode(PhysicsMode mode) + { + _physicsMode = mode; + + //Disable. + if (mode == PhysicsMode.Disabled || mode == PhysicsMode.TimeManager) + { + Physics.autoSimulation = false; +#if !UNITY_2020_2_OR_NEWER + Physics2D.autoSimulation = false; +#else + Physics2D.simulationMode = SimulationMode2D.Script; +#endif + } + //Automatically simulate. + else + { + Physics.autoSimulation = true; +#if !UNITY_2020_2_OR_NEWER + Physics2D.autoSimulation = true; +#else + Physics2D.simulationMode = SimulationMode2D.FixedUpdate; +#endif + } + } + + #region PingPong. + /// + /// Modifies client ping based on LocalTick and clientTIck. + /// + /// + internal void ModifyPing(uint clientTick) + { + uint tickDifference = (LocalTick - clientTick); + _pingAverage.ComputeAverage(tickDifference); + double averageInTime = (_pingAverage.Average * TickDelta * 1000); + RoundTripTime = (long)Math.Round(averageInTime); + _receivedPong = true; + + OnRoundTripTimeUpdated?.Invoke(RoundTripTime); + } + + /// + /// Sends a ping to the server. + /// + private void TrySendPing(uint? tickOverride = null) + { + byte pingInterval = PingInterval; + + /* How often client may send ping is based on if + * the server responded to the last ping. + * A response may not be received if the server + * believes the client is pinging too fast, or if the + * client is having difficulties reaching the server. */ + long requiredTime = (pingInterval * 1000); + float multiplier = (_receivedPong) ? 1f : 1.5f; + + requiredTime = (long)(requiredTime * multiplier); + uint requiredTicks = TimeToTicks(pingInterval * multiplier); + + _pingTicks++; + /* We cannot just consider time because ticks might run slower + * from adjustments. We also cannot only consider ticks because + * they might run faster from adjustments. Therefor require both + * to have pass checks. */ + if (_pingTicks < requiredTicks || _pingStopwatch.ElapsedMilliseconds < requiredTime) + return; + + _pingTicks = 0; + _pingStopwatch.Restart(); + //Unset receivedPong, wait for new response. + _receivedPong = false; + + uint tick = (tickOverride == null) ? LocalTick : tickOverride.Value; + using (PooledWriter writer = WriterPool.GetWriter()) + { + writer.WritePacketId(PacketId.PingPong); + writer.WriteUInt32(tick, AutoPackType.Unpacked); + _networkManager.TransportManager.SendToServer((byte)Channel.Unreliable, writer.GetArraySegment()); + } + } + + /// + /// Sends a pong to a client. + /// + internal void SendPong(NetworkConnection conn, uint clientTick) + { + if (!conn.IsActive || !conn.Authenticated) + return; + + using (PooledWriter writer = WriterPool.GetWriter()) + { + writer.WritePacketId(PacketId.PingPong); + writer.WriteUInt32(clientTick, AutoPackType.Unpacked); + conn.SendToClient((byte)Channel.Unreliable, writer.GetArraySegment()); + } + } + #endregion + + /// + /// Increases the tick based on simulation rate. + /// + private void IncreaseTick() + { + bool isClient = _networkManager.IsClient; + bool isServer = _networkManager.IsServer; + + double tickDelta = TickDelta; + double timePerSimulation = (isServer) ? tickDelta : _adjustedTickDelta; + double time = Time.unscaledDeltaTime; + _elapsedTickTime += time; + FrameTicked = (_elapsedTickTime >= timePerSimulation); + + //Number of ticks to occur this frame. + int ticksCount = Mathf.FloorToInt((float)(_elapsedTickTime / timePerSimulation)); + if (ticksCount > 1) + _lastMultipleTicksTime = Time.unscaledDeltaTime; + + if (_allowTickDropping && !_networkManager.IsServer) + { + //If ticks require dropping. Set exactly to maximum ticks. + if (ticksCount > _maximumFrameTicks) + _elapsedTickTime = (timePerSimulation * (double)_maximumFrameTicks); + } + + bool variableTiming = (_timingType == TimingType.Variable); + bool frameTicked = FrameTicked; + + do + { + if (frameTicked) + { + _elapsedTickTime -= timePerSimulation; + OnPreTick?.Invoke(); + } + + /* This has to be called inside the loop because + * OnPreTick promises data hasn't been read yet. + * Therefor iterate must occur after OnPreTick. + * Iteration will only run once per frame. */ + if (frameTicked || variableTiming) + TryIterateData(true); + + if (frameTicked) + { + OnTick?.Invoke(); + + if (PhysicsMode == PhysicsMode.TimeManager) + { + float tick = (float)TickDelta; + OnPrePhysicsSimulation?.Invoke(tick); + Physics.Simulate(tick); + Physics2D.Simulate(tick); + OnPostPhysicsSimulation?.Invoke(tick); + } + + OnPostTick?.Invoke(); + /* If isClient this is the + * last tick during this loop. */ + if (isClient && (_elapsedTickTime < timePerSimulation)) + { + _networkManager.ClientManager.SendLodUpdate(false); + TrySendPing(LocalTick + 1); + } + + if (_networkManager.IsServer) + SendTimingAdjustment(); + } + + //Send out data. + if (frameTicked || variableTiming) + TryIterateData(false); + + if (frameTicked) + { + if (_networkManager.IsClient) + _clientTicks++; + + Tick++; + LocalTick++; + + _networkManager.ObserverManager.CalculateLevelOfDetail(LocalTick); + } + + } while (_elapsedTickTime >= timePerSimulation); + } + + + + #region Tick conversions. + /// + /// Returns the percentage of how far the TimeManager is into the next tick. + /// + /// + public double GetTickPercent() + { + if (_networkManager == null) + return default; + + double delta = (_networkManager.IsServer) ? TickDelta : _adjustedTickDelta; + double percent = (_elapsedTickTime / delta) * 100d; + return percent; + } + /// + /// Returns a PreciseTick. + /// + /// Tick to set within the returned PreciseTick. + /// + public PreciseTick GetPreciseTick(uint tick) + { + if (_networkManager == null) + return default; + + double delta = (_networkManager.IsServer) ? TickDelta : _adjustedTickDelta; + double percent = (_elapsedTickTime / delta) * 100; + + return new PreciseTick(tick, percent); + } + /// + /// Returns a PreciseTick. + /// + /// Tick to use within PreciseTick. + /// + public PreciseTick GetPreciseTick(TickType tickType) + { + if (_networkManager == null) + return default; + + if (tickType == TickType.Tick) + { + return GetPreciseTick(Tick); + } + else if (tickType == TickType.LocalTick) + { + return GetPreciseTick(LocalTick); + } + else if (tickType == TickType.LastPacketTick) + { + return GetPreciseTick(LastPacketTick); + } + else + { + _networkManager.LogError($"TickType {tickType.ToString()} is unhandled."); + return default; + } + } + + + /// + /// Converts current ticks to time. + /// + /// TickType to compare against. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double TicksToTime(TickType tickType = TickType.LocalTick) + { + if (tickType == TickType.LocalTick) + { + return TicksToTime(LocalTick); + } + else if (tickType == TickType.Tick) + { + return TicksToTime(Tick); + } + else if (tickType == TickType.LastPacketTick) + { + return TicksToTime(LastPacketTick); + } + else + { + _networkManager.LogError($"TickType {tickType} is unhandled."); + return 0d; + } + } + + /// + /// Converts a number ticks to time. + /// + /// Ticks to convert. + /// + public double TicksToTime(uint ticks) + { + return (TickDelta * (double)ticks); + } + + /// + /// Gets time passed from currentTick to previousTick. + /// + /// The current tick. + /// The previous tick. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double TimePassed(uint currentTick, uint previousTick) + { + double multiplier; + double result; + if (currentTick >= previousTick) + { + multiplier = 1f; + result = TicksToTime(currentTick - previousTick); + } + else + { + multiplier = -1f; + result = TicksToTime(previousTick - currentTick); + } + + return (result * multiplier); + } + + /// + /// Gets time passed from Tick to preciseTick. + /// + /// PreciseTick value to compare against. + /// True to allow negative values. When false and value would be negative 0 is returned. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double TimePassed(PreciseTick preciseTick, bool allowNegative = false) + { + PreciseTick currentPt = GetPreciseTick(TickType.Tick); + + long tickDifference = (currentPt.Tick - preciseTick.Tick); + double percentDifference = (currentPt.Percent - preciseTick.Percent); + + /* If tickDifference is less than 0 or tickDifference and percentDifference are 0 or less + * then the result would be negative. */ + bool negativeValue = (tickDifference < 0 || (tickDifference <= 0 && percentDifference <= 0)); + + if (!allowNegative && negativeValue) + return 0d; + + double tickTime = TimePassed(preciseTick.Tick, true); + double percent = (percentDifference / 100); + double percentTime = (percent * TickDelta); + + return (tickTime + percentTime); + } + /// + /// Gets time passed from Tick to previousTick. + /// + /// The previous tick. + /// True to allow negative values. When false and value would be negative 0 is returned. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double TimePassed(uint previousTick, bool allowNegative = false) + { + uint currentTick = Tick; + //Difference will be positive. + if (currentTick >= previousTick) + { + return TicksToTime(currentTick - previousTick); + } + //Difference would be negative. + else + { + if (!allowNegative) + { + return 0d; + } + else + { + double difference = TicksToTime(previousTick - currentTick); + return (difference * -1d); + } + } + } + + /// + /// Converts time to ticks. + /// + /// Time to convert. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public uint TimeToTicks(double time, TickRounding rounding = TickRounding.RoundNearest) + { + double result = (time / TickDelta); + + if (rounding == TickRounding.RoundNearest) + return (uint)Math.Round(result); + else if (rounding == TickRounding.RoundDown) + return (uint)Math.Floor(result); + else + return (uint)Math.Ceiling(result); + } + + /// + /// Estimatedly converts a synchronized tick to what it would be for the local tick. + /// + /// Synchronized tick to convert. + /// + public uint TickToLocalTick(uint tick) + { + //Server will always have local and tick aligned. + if (_networkManager.IsServer) + return tick; + + long difference = (Tick - tick); + //If no ticks have passed then return current local tick. + if (difference <= 0) + return LocalTick; + + long result = (LocalTick - difference); + if (result <= 0) + result = 0; + + return (uint)result; + } + /// + /// Estimatedly converts a local tick to what it would be for the synchronized tick. + /// + /// Local tick to convert. + /// + public uint LocalTickToTick(uint localTick) + { + //Server will always have local and tick aligned. + if (_networkManager.IsServer) + return localTick; + + long difference = (LocalTick - localTick); + //If no ticks have passed then return current local tick. + if (difference <= 0) + return Tick; + + long result = (Tick - difference); + if (result <= 0) + result = 0; + + return (uint)result; + + } + #endregion + + + /// + /// Tries to iterate incoming or outgoing data. + /// + /// True to iterate incoming. + private void TryIterateData(bool incoming) + { + if (incoming) + { + /* It's not possible for data to come in + * more than once per frame but there could + * be new data going out each tick, since + * movement is often based off the tick system. + * Because of this don't iterate incoming if + * it's the same frame but the outgoing + * may iterate multiple times per frame. */ + int frameCount = Time.frameCount; + if (frameCount == _lastIncomingIterationFrame) + return; + _lastIncomingIterationFrame = frameCount; + + _networkManager.TransportManager.IterateIncoming(true); + _networkManager.TransportManager.IterateIncoming(false); + } + else + { + _networkManager.TransportManager.IterateOutgoing(true); + _networkManager.TransportManager.IterateOutgoing(false); + } + } + + + #region Timing adjusting. + /// + /// Sends a TimingUpdate packet to clients. + /// + private void SendTimingAdjustment() + { + uint requiredTicks = TimeToTicks(_timingInterval); + uint tick = Tick; + if (tick - _lastUpdateTicks >= requiredTicks) + { + //Now send using a packetId. + PooledWriter writer = WriterPool.GetWriter(); + writer.WritePacketId(PacketId.TimingUpdate); + _networkManager.TransportManager.SendToClients((byte)Channel.Unreliable, writer.GetArraySegment()); + writer.Dispose(); + + _lastUpdateTicks = tick; + } + } + + /// + /// Called on client when server sends a timing update. + /// + /// + internal void ParseTimingUpdate() + { + //Don't adjust timing on server. + if (_networkManager.IsServer) + return; + + //Add half of rtt onto tick. + uint rttTicks = TimeToTicks((RoundTripTime / 2) / 1000f); + Tick = LastPacketTick + rttTicks; + uint expected = (uint)(TickRate * _timingInterval); + long difference; + //If ticking too fast. + if (_clientTicks > expected) + difference = (long)(_clientTicks - expected); + //Not ticking fast enough. + else + difference = (long)((expected - _clientTicks) * -1); + + //If difference is unusually off then reset timings. + if (Mathf.Abs(difference) >= RESET_STEPS_THRESHOLD) + { + _adjustedTickDelta = TickDelta; + } + else + { + sbyte steps = (sbyte)Mathf.Clamp(difference, sbyte.MinValue, sbyte.MaxValue); + double percent = (steps < 0) ? CLIENT_SPEEDUP_PERCENT : CLIENT_SLOWDOWN_PERCENT; + double change = (steps * (percent * TickDelta)); + + _adjustedTickDelta = MathFN.ClampDouble(_adjustedTickDelta + change, _clientTimingRange[0], _clientTimingRange[1]); + } + + _clientTicks = 0; + } + #endregion + + /// + /// Sets the TickRate to use. This value is not synchronized, it must be set on client and server independently. + /// + /// New TickRate to use. + public void SetTickRate(ushort value) + { + TickRate = value; + TickDelta = (1d / TickRate); + _adjustedTickDelta = TickDelta; + _clientTimingRange = new double[] + { + TickDelta * (1f - CLIENT_TIMING_PERCENT_RANGE), + TickDelta * (1f + CLIENT_TIMING_PERCENT_RANGE) + }; + } + + #region UNITY_EDITOR + private void OnValidate() + { + SetInitialValues(); + } + #endregion + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs.meta b/Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs.meta new file mode 100644 index 0000000..5f8b64f --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3fdaae44044276a49a52229c1597e33b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Transporting.meta b/Assets/FishNet/Runtime/Managing/Transporting.meta new file mode 100644 index 0000000..089da21 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Transporting.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dadeb2231e0f13b418e05dfc6c4321f6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Transporting/IntermediateLayer.cs b/Assets/FishNet/Runtime/Managing/Transporting/IntermediateLayer.cs new file mode 100644 index 0000000..2836b59 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Transporting/IntermediateLayer.cs @@ -0,0 +1,36 @@ + +using System; +using UnityEngine; + +namespace FishNet.Managing.Transporting +{ + + /// + /// When inherited from this may be used with the TransportManager to alter messages before they are sent and received. + /// + public abstract class IntermediateLayer : MonoBehaviour + { + /// + /// TransportManager associated with this script. + /// + public TransportManager TransportManager { get; private set; } + + /// + /// Called when data is received. + /// + /// Original data. + /// True if receiving from the server, false if from a client. + /// Modified data. + public abstract ArraySegment HandleIncoming(ArraySegment src, bool fromServer); + /// + /// Called when data is sent. + /// + /// Original data. + /// True if sending to the server, false if to a client. + /// Modified data. + public abstract ArraySegment HandleOutoing(ArraySegment src, bool toServer); + + internal void InitializeOnce(TransportManager manager) => TransportManager = manager; + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Transporting/IntermediateLayer.cs.meta b/Assets/FishNet/Runtime/Managing/Transporting/IntermediateLayer.cs.meta new file mode 100644 index 0000000..c6a1f1c --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Transporting/IntermediateLayer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3f8a2e0f9aaea614887f5f7b15350e46 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Transporting/LatencySimulator.cs b/Assets/FishNet/Runtime/Managing/Transporting/LatencySimulator.cs new file mode 100644 index 0000000..ef252f9 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Transporting/LatencySimulator.cs @@ -0,0 +1,376 @@ +using FishNet.Connection; +using FishNet.Transporting; +using FishNet.Utility.Performance; +using System; +using System.Collections.Generic; +using UnityEngine; + +//Thanks to TiToMoskito originally creating this as a Transport. +//https://github.com/TiToMoskito/FishyLatency +namespace FishNet.Managing.Transporting +{ + [System.Serializable] + public class LatencySimulator + { + #region Types. + /// + /// A message affected by latency. + /// + private struct Message + { + public readonly int ConnectionId; + public readonly byte[] Data; + public readonly int Length; + public readonly float SendTime; + + public Message(int connectionId, ArraySegment segment, float latency) + { + this.ConnectionId = connectionId; + this.SendTime = (Time.unscaledTime + latency); + this.Length = segment.Count; + this.Data = ByteArrayPool.Retrieve(this.Length); + Buffer.BlockCopy(segment.Array, segment.Offset, this.Data, 0, this.Length); + } + + public ArraySegment GetSegment() + { + return new ArraySegment(Data, 0, Length); + } + } + #endregion + + #region Internal. + /// + /// True if latency can be simulated. + /// + internal bool CanSimulate => (GetEnabled() && (GetLatency() > 0 || GetPacketLost() > 0 || GetOutOfOrder() > 0)); + #endregion + + #region Serialized + [Header("Settings")] + /// + /// + /// + [Tooltip("True if latency simulator is enabled.")] + [SerializeField] + private bool _enabled; + /// + /// Gets the enabled value of simulator. + /// + public bool GetEnabled() => _enabled; + /// + /// Sets the enabled value of simulator. + /// + /// New value. + public void SetEnabled(bool value) + { + if (value == _enabled) + return; + + _enabled = value; + Reset(); + } + /// + /// + /// + [Tooltip("True to add latency on clientHost as well.")] + [SerializeField] + private bool _simulateHost = true; + /// + /// Milliseconds to add between packets. When acting as host this value will be doubled. Added latency will be a minimum of tick rate. + /// + [Tooltip("Milliseconds to add between packets. When acting as host this value will be doubled. Added latency will be a minimum of tick rate.")] + [Range(0, 60000)] + [SerializeField] + private long _latency = 0; + /// + /// Gets the latency value. + /// + /// + public long GetLatency() => _latency; + /// + /// Sets a new latency value. + /// + /// Latency as milliseconds. + public void SetLatency(long value) => _latency = value; + + [Header("Unreliable")] + /// + /// Percentage of unreliable packets which should arrive out of order. + /// + [Tooltip("Percentage of unreliable packets which should arrive out of order.")] + [Range(0f, 1f)] + [SerializeField] + private double _outOfOrder = 0; + /// + /// Out of order chance, 1f is a 100% chance to occur. + /// + /// + public double GetOutOfOrder() => _outOfOrder; + /// + /// Sets out of order chance. 1f is a 100% chance to occur. + /// + /// New Value. + public void SetOutOfOrder(double value) => _outOfOrder = value; + /// + /// Percentage of packets which should drop. + /// + [Tooltip("Percentage of packets which should drop.")] + [Range(0, 1)] + [SerializeField] + private double _packetLoss = 0; + /// + /// Gets packet loss chance. 1f is a 100% chance to occur. + /// + /// + public double GetPacketLost() => _packetLoss; + /// + /// Sets packet loss chance. 1f is a 100% chance to occur. + /// + /// New Value. + public void SetPacketLoss(double value) => _packetLoss = value; + #endregion + + #region Private + /// + /// Transport to send data on. + /// + private Transport _transport; + /// + /// Reliable messages to the server. + /// + private List _toServerReliable = new List(); + /// + /// Unreliable messages to the server. + /// + private List _toServerUnreliable = new List(); + /// + /// Reliable messages to clients. + /// + private List _toClientReliable = new List(); + /// + /// Unreliable messages to clients. + /// + private List _toClientUnreliable = new List(); + /// + /// NetworkManager for this instance. + /// + private NetworkManager _networkManager; + /// + /// Used to generate chances of latency. + /// + private readonly System.Random _random = new System.Random(); + #endregion + + #region Initialization and Unity + public void Initialize(NetworkManager manager, Transport transport) + { + _networkManager = manager; + _transport = transport; + } + #endregion + + /// + /// Stops both client and server. + /// + public void Reset() + { + bool enabled = GetEnabled(); + if (_transport != null && enabled) + { + IterateAndStore(_toServerReliable); + IterateAndStore(_toServerUnreliable); + IterateAndStore(_toClientReliable); + IterateAndStore(_toClientUnreliable); + } + + void IterateAndStore(List messages) + { + foreach (Message m in messages) + { + _transport.SendToServer((byte)Channel.Reliable, m.GetSegment()); + ByteArrayPool.Store(m.Data); + } + } + + _toServerReliable.Clear(); + _toServerUnreliable.Clear(); + _toClientReliable.Clear(); + _toClientUnreliable.Clear(); + } + + /// + /// Removes pending or held packets for a connection. + /// + /// Connection to remove pending packets for. + public void RemovePendingForConnection(int connectionId) + { + RemoveFromCollection(_toServerUnreliable); + RemoveFromCollection(_toServerUnreliable); + RemoveFromCollection(_toClientReliable); + RemoveFromCollection(_toClientUnreliable); + + void RemoveFromCollection(List c) + { + for (int i = 0; i < c.Count; i++) + { + if (c[i].ConnectionId == connectionId) + { + c.RemoveAt(i); + i--; + } + } + } + } + + #region Simulation + /// + /// Returns long latency as a float. + /// + /// + /// + private float GetLatencyAsFloat() + { + return (float)(_latency / 1000f); + } + + /// + /// Adds a packet for simulation. + /// + public void AddOutgoing(byte channelId, ArraySegment segment, bool toServer = true, int connectionId = -1) + { + /* If to not simulate for host see if this packet + * should be sent normally. */ + if (!_simulateHost && _networkManager != null && _networkManager.IsHost) + { + /* If going to the server and is host then + * it must be sent from clientHost. */ + if (toServer) + { + _transport.SendToServer(channelId, segment); + return; + } + //Not to server, see if going to clientHost. + else + { + //If connId is the same as clientHost id. + if (_networkManager.ClientManager.Connection.ClientId == connectionId) + { + _transport.SendToClient(channelId, segment, connectionId); + return; + } + } + } + + List collection; + Channel c = (Channel)channelId; + + if (toServer) + collection = (c == Channel.Reliable) ? _toServerReliable : _toServerUnreliable; + else + collection = (c == Channel.Reliable) ? _toClientReliable : _toClientUnreliable; + + float latency = GetLatencyAsFloat(); + //If dropping check to add extra latency if reliable, or discard if not. + if (DropPacket()) + { + if (c == Channel.Reliable) + { + latency += (latency * 0.3f); //add extra for resend. + } + //If not reliable then return the segment array to pool. + else + { + return; + } + } + + Message msg = new Message(connectionId, segment, latency); + int count = collection.Count; + if (c == Channel.Unreliable && count > 0 && OutOfOrderPacket(c)) + collection.Insert(count - 1, msg); + else + collection.Add(msg); + } + + /// + /// Simulates pending outgoing packets. + /// + /// True if sending to the server. + public void IterateOutgoing(bool toServer) + { + if (_transport == null) + { + Reset(); + return; + } + + if (toServer) + { + IterateCollection(_toServerReliable, Channel.Reliable); + IterateCollection(_toServerUnreliable, Channel.Unreliable); + } + else + { + IterateCollection(_toClientReliable, Channel.Reliable); + IterateCollection(_toClientUnreliable, Channel.Unreliable); + } + + void IterateCollection(List collection, Channel channel) + { + byte cByte = (byte)channel; + float unscaledTime = Time.unscaledTime; + + int count = collection.Count; + int iterations = 0; + for (int i = 0; i < count; i++) + { + Message msg = collection[i]; + //Not enough time has passed. + if (unscaledTime < msg.SendTime) + break; + + if (toServer) + _transport.SendToServer(cByte, msg.GetSegment()); + else + _transport.SendToClient(cByte, msg.GetSegment(), msg.ConnectionId); + + iterations++; + } + + if (iterations > 0) + { + for (int i = 0; i < iterations; i++) + ByteArrayPool.Store(collection[i].Data); + collection.RemoveRange(0, iterations); + } + } + + _transport.IterateOutgoing(toServer); + } + + /// + /// Returns if a packet should drop. + /// + /// + private bool DropPacket() + { + return (_packetLoss > 0d && (_random.NextDouble() < _packetLoss)); + } + + /// + /// Returns if a packet should be out of order. + /// + /// + /// + private bool OutOfOrderPacket(Channel c) + { + if (c == Channel.Reliable) + return false; + + return (_outOfOrder > 0d && (_random.NextDouble() < _outOfOrder)); + } + #endregion + } +} + diff --git a/Assets/FishNet/Runtime/Managing/Transporting/LatencySimulator.cs.meta b/Assets/FishNet/Runtime/Managing/Transporting/LatencySimulator.cs.meta new file mode 100644 index 0000000..7eb5b8b --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Transporting/LatencySimulator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 82bfafe804acb534fbf04c88de6eeed1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Transporting/SplitReader.cs b/Assets/FishNet/Runtime/Managing/Transporting/SplitReader.cs new file mode 100644 index 0000000..704f859 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Transporting/SplitReader.cs @@ -0,0 +1,99 @@ +using FishNet.Serializing; +using System; +using UnityEngine; + +namespace FishNet.Managing.Transporting +{ + + internal class SplitReader + { + #region Private. + /// + /// Tick split is for. + /// Tick must be a negative value so that it's impossible for the first tick to align. + /// + private long _tick = -1; + /// + /// Expected number of splits. + /// + private int _expectedMessages; + /// + /// Number of splits received so far. + /// + private ushort _receivedMessages; + /// + /// Writer containing split packet combined. + /// + private PooledWriter _writer = WriterPool.GetWriter(); + #endregion + + internal SplitReader() + { + //Increase capacity to reduce the chance of resizing. + _writer.EnsureBufferCapacity(20000); + } + + /// + /// Gets split header values. + /// + internal void GetHeader(PooledReader reader, out int expectedMessages) + { + expectedMessages = reader.ReadInt32(); + } + + /// + /// Combines split data. + /// + internal void Write(uint tick, PooledReader reader, int expectedMessages) + { + //New tick which means new split. + if (tick != _tick) + Reset(tick, expectedMessages); + + /* This is just a guess as to how large the end + * message could be. If the writer is not the minimum + * of this length then resize it. */ + int estimatedBufferSize = (expectedMessages * 1500); + if (_writer.Capacity < estimatedBufferSize) + _writer.EnsureBufferCapacity(estimatedBufferSize); + /* Empty remainder of reader into the writer. + * It does not matter if parts of the reader + * contain data added after the split because + * once the split is fully combined the data + * is parsed as though it came in as one message, + * which is how data is normally read. */ + ArraySegment data = reader.ReadArraySegment(reader.Remaining); + _writer.WriteArraySegment(data); + _receivedMessages++; + } + + /// + /// Returns if all split messages have been received. + /// + /// + internal ArraySegment GetFullMessage() + { + if (_receivedMessages < _expectedMessages) + { + return default(ArraySegment); + } + else + { + ArraySegment segment = _writer.GetArraySegment(); + Reset(); + return segment; + } + } + + private void Reset(uint tick = 0, int expectedMessages = 0) + { + _tick = tick; + _receivedMessages = 0; + _expectedMessages = expectedMessages; + _writer.Reset(); + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Transporting/SplitReader.cs.meta b/Assets/FishNet/Runtime/Managing/Transporting/SplitReader.cs.meta new file mode 100644 index 0000000..3c123e3 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Transporting/SplitReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a1c06be1540b77842be35823aa54b19b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.QOL.cs b/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.QOL.cs new file mode 100644 index 0000000..3cf8b76 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.QOL.cs @@ -0,0 +1,65 @@ +using FishNet.Transporting; +using FishNet.Transporting.Multipass; +using UnityEngine; + +namespace FishNet.Managing.Transporting +{ + + /// + /// Communicates with the Transport to send and receive data. + /// + public sealed partial class TransportManager : MonoBehaviour + { + #region Public. + /// + /// Returns IsLocalTransport for the current transport. + /// + public bool IsLocalTransport(int connectionId) => (Transport == null) ? false : Transport.IsLocalTransport(connectionId); + #endregion + + + /// + /// Gets transport on index. + /// Commonly index will be 0 unless using Multipass. + /// + /// + public Transport GetTransport(int index) + { + //If using multipass try to find the correct transport. + if (Transport is Multipass mp) + { + return mp.GetTransport(index); + } + //Not using multipass. + else + { + return Transport; + } + } + + /// + /// Gets transport of type T. + /// + /// Returns the found transport which is of type T. Returns default of T if not found. + public T GetTransport() where T : Transport + { + //If using multipass try to find the correct transport. + if (Transport is Multipass mp) + { + if (typeof(T) == typeof(Multipass)) + return (T)(object)mp; + else + return mp.GetTransport(); + } + //Not using multipass. + else + { + if (Transport.GetType() == typeof(T)) + return (T)(object)Transport; + else + return default(T); + } + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.QOL.cs.meta b/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.QOL.cs.meta new file mode 100644 index 0000000..21ee1b3 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.QOL.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aac8eab4e511d7e4dbc81eb74aea7f23 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.cs b/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.cs new file mode 100644 index 0000000..b09545b --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.cs @@ -0,0 +1,668 @@ +using FishNet.Connection; +using FishNet.Managing.Timing; +using FishNet.Serializing; +using FishNet.Transporting; +using FishNet.Transporting.Multipass; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Managing.Transporting +{ + + /// + /// Communicates with the Transport to send and receive data. + /// + [DisallowMultipleComponent] + [AddComponentMenu("FishNet/Manager/TransportManager")] + public sealed partial class TransportManager : MonoBehaviour + { + #region Types. + private struct DisconnectingClient + { + public uint Tick; + public NetworkConnection Connection; + + public DisconnectingClient(uint tick, NetworkConnection connection) + { + Tick = tick; + Connection = connection; + } + } + #endregion + + #region Public. + /// + /// Called before IterateOutgoing has started. + /// + internal event Action OnIterateOutgoingStart; + /// + /// Called after IterateOutgoing has completed. + /// + internal event Action OnIterateOutgoingEnd; + /// + /// Called before IterateIncoming has started. True for on server, false for on client. + /// + internal event Action OnIterateIncomingStart; + /// + /// Called after IterateIncoming has completed. True for on server, false for on client. + /// + internal event Action OnIterateIncomingEnd; + /// + /// The current Transport being used. + /// + [Tooltip("The current Transport being used.")] + public Transport Transport; + #endregion + + #region Serialized. + /// + /// Layer used to modify data before it is sent or received. + /// + [Tooltip("Layer used to modify data before it is sent or received.")] + [SerializeField] + private IntermediateLayer _intermediateLayer; + /// + /// + /// + [Tooltip("Latency simulation settings.")] + [SerializeField] + private LatencySimulator _latencySimulator = new LatencySimulator(); + /// + /// Latency simulation settings. + /// + public LatencySimulator LatencySimulator + { + get + { + //Shouldn't ever be null unless the user nullifies it. + if (_latencySimulator == null) + _latencySimulator = new LatencySimulator(); + return _latencySimulator; + } + } + #endregion + + #region Private. + /// + /// NetworkConnections on the server which have to send data to clients. + /// + private List _dirtyToClients = new List(); + /// + /// PacketBundles to send to the server. + /// + private List _toServerBundles = new List(); + /// + /// NetworkManager handling this TransportManager. + /// + private NetworkManager _networkManager; + /// + /// Clients which are pending disconnects. + /// + private List _disconnectingClients = new List(); + /// + /// Lowest MTU of all transports for channels. + /// + private int[] _lowestMtu; + #endregion + + #region Consts. + /// + /// Number of bytes sent for PacketId. + /// + public const byte PACKET_ID_BYTES = 2; + /// + /// Number of bytes sent for ObjectId. + /// + public const byte OBJECT_ID_BYTES = 2; + /// + /// Number of bytes sent for ComponentIndex. + /// + public const byte COMPONENT_INDEX_BYTES = 1; + /// + /// Number of bytes sent for Tick. + /// + public const byte TICK_BYTES = 4; + /// + /// Number of bytes sent to indicate split count. + /// + private const byte SPLIT_COUNT_BYTES = 4; + /// + /// Number of bytes required for split data. + /// + public const byte SPLIT_INDICATOR_SIZE = (PACKET_ID_BYTES + SPLIT_COUNT_BYTES); + /// + /// Number of channels supported. + /// + public const byte CHANNEL_COUNT = 2; + #endregion + + /// + /// Initializes this script for use. + /// + internal void InitializeOnce_Internal(NetworkManager manager) + { + _networkManager = manager; + /* If transport isn't specified then add default + * transport. */ + if (Transport == null && !gameObject.TryGetComponent(out Transport)) + Transport = gameObject.AddComponent(); + + Transport.Initialize(_networkManager, 0); + //Cache lowest Mtus. + _lowestMtu = new int[CHANNEL_COUNT]; + for (byte i = 0; i < CHANNEL_COUNT; i++) + _lowestMtu[i] = GetLowestMTU(i); + + InitializeToServerBundles(); + if (_intermediateLayer != null) + _intermediateLayer.InitializeOnce(this); +#if UNITY_EDITOR || DEVELOPMENT_BUILD + _latencySimulator.Initialize(manager, Transport); +#endif + } + + /// + /// Sets a connection from server to client dirty. + /// + /// + internal void ServerDirty(NetworkConnection conn) + { + _dirtyToClients.Add(conn); + } + + /// + /// Initializes ToServerBundles for use. + /// + private void InitializeToServerBundles() + { + /* For ease of use FishNet will always have + * only two channels, reliable and unreliable. + * Even if the transport only supports reliable + * also setup for unreliable. */ + for (byte i = 0; i < CHANNEL_COUNT; i++) + { + int mtu = GetLowestMTU(i); + _toServerBundles.Add(new PacketBundle(_networkManager, mtu)); + } + } + + #region GetMTU. + /* Returned MTUs are always -1 to allow an extra byte + * to specify channel where certain transports do + * not allow or provide channel information. */ + /// + /// Returns the lowest MTU for a channel. When using multipass this will evaluate all transports within Multipass. + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetLowestMTU(byte channel) + { + //Use cached if available. + if (_lowestMtu[channel] > 0) + return _lowestMtu[channel]; + + if (Transport is Multipass mp) + { + int? lowestMtu = null; + foreach (Transport t in mp.Transports) + { + int thisMtu = t.GetMTU(channel); + if (lowestMtu == null || thisMtu < lowestMtu.Value) + lowestMtu = thisMtu; + } + + //If lowest was not changed return unset. + if (lowestMtu == null) + { + return -1; + } + else + { + int mtu = lowestMtu.Value; + if (mtu >= 0) + mtu -= 1; + return mtu; + } + } + else + { + return GetMTU(channel); + } + } + /// + /// Gets MTU on the current transport for channel. + /// + /// Channel to get MTU of. + /// + public int GetMTU(byte channel) + { + int mtu = Transport.GetMTU(channel); + if (mtu >= 0) + mtu -= 1; + return mtu; + } + /// + /// Gets MTU on the transportIndex for channel. This requires use of Multipass. + /// + /// Index of the transport to get the MTU on. + /// Channel to get MTU of. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetMTU(int transportIndex, byte channel) + { + if (Transport is Multipass mp) + { + int mtu = mp.GetMTU(channel, transportIndex); + if (mtu >= 0) + mtu -= 1; + return mtu; + } + //Using first/only transport. + else if (transportIndex == 0) + { + return GetMTU(channel); + } + //Unhandled. + else + { + _networkManager.LogWarning($"MTU cannot be returned with transportIndex because {typeof(Multipass).Name} is not in use."); + return -1; + } + } + /// + /// Gets MTU on the transport type for channel. This requires use of Multipass. + /// + /// Tyep of transport to use. + /// Channel to get MTU of. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetMTU(byte channel) where T : Transport + { + Transport transport = GetTransport(); + if (transport != null) + { + int mtu = transport.GetMTU(channel); + if (mtu >= 0) + mtu -= 1; + return mtu; + } + + //Fall through. + return -1; + } + #endregion + + /// + /// Passes received to the intermediate layer. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ArraySegment ProcessIntermediateIncoming(ArraySegment src, bool fromServer) + { + return (_intermediateLayer == null) ? src : _intermediateLayer.HandleIncoming(src, fromServer); + } + /// + /// Passes sent to the intermediate layer. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ArraySegment ProcessIntermediateOutgoing(ArraySegment src, bool toServer) + { + return (_intermediateLayer == null) ? src : _intermediateLayer.HandleOutoing(src, toServer); + } + + /// + /// Sends data to a client. + /// + /// Channel to send on. + /// Data to send. + /// Connection to send to. Use null for all clients. + /// True to split large packets which exceed MTU and send them in order on the reliable channel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void SendToClient(byte channelId, ArraySegment segment, NetworkConnection connection, bool splitLargeMessages = true) + { + segment = ProcessIntermediateOutgoing(segment, false); + SetSplitValues(channelId, segment, splitLargeMessages, out int requiredSplitMessages, out int maxSplitMessageSize); + SendToClient_Internal(channelId, segment, connection, requiredSplitMessages, maxSplitMessageSize); + } + private void SendToClient_Internal(byte channelId, ArraySegment segment, NetworkConnection connection, int requiredSplitMessages, int maxSplitMessageSize) + { + if (connection == null) + return; + + if (requiredSplitMessages > 1) + SendSplitData(connection, ref segment, requiredSplitMessages, maxSplitMessageSize); + else + connection.SendToClient(channelId, segment); + } + + /// + /// Sends data to observers. + /// + /// + /// + /// + /// True to split large packets which exceed MTU and send them in order on the reliable channel. + internal void SendToClients(byte channelId, ArraySegment segment, HashSet observers, HashSet excludedConnections = null, bool splitLargeMessages = true) + { + segment = ProcessIntermediateOutgoing(segment, false); + SetSplitValues(channelId, segment, splitLargeMessages, out int requiredSplitMessages, out int maxSplitMessageSize); + SendToClients_Internal(channelId, segment, observers, excludedConnections, requiredSplitMessages, maxSplitMessageSize); + } + private void SendToClients_Internal(byte channelId, ArraySegment segment, HashSet observers, HashSet excludedConnections, int requiredSplitMessages, int maxSplitMessageSize) + { + if (excludedConnections == null || excludedConnections.Count == 0) + { + foreach (NetworkConnection conn in observers) + SendToClient_Internal(channelId, segment, conn, requiredSplitMessages, maxSplitMessageSize); + } + else + { + foreach (NetworkConnection conn in observers) + { + if (excludedConnections.Contains(conn)) + continue; + SendToClient_Internal(channelId, segment, conn, requiredSplitMessages, maxSplitMessageSize); + } + } + } + + + + /// + /// Sends data to all clients. + /// + /// Channel to send on. + /// Data to send. + /// True to split large packets which exceed MTU and send them in order on the reliable channel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void SendToClients(byte channelId, ArraySegment segment, bool splitLargeMessages = true) + { + segment = ProcessIntermediateOutgoing(segment, false); + SetSplitValues(channelId, segment, splitLargeMessages, out int requiredSplitMessages, out int maxSplitMessageSize); + SendToClients_Internal(channelId, segment, requiredSplitMessages, maxSplitMessageSize); + } + private void SendToClients_Internal(byte channelId, ArraySegment segment, int requiredSplitMessages, int maxSplitMessageSize) + { + /* Rather than buffer the message once and send to every client + * it must be queued into every client. This ensures clients + * receive the message in order of other packets being + * delivered to them. */ + foreach (NetworkConnection conn in _networkManager.ServerManager.Clients.Values) + SendToClient_Internal(channelId, segment, conn, requiredSplitMessages, maxSplitMessageSize); + } + + /// + /// Sends data to the server. + /// + /// Channel to send on. + /// Data to send. + /// True to split large packets which exceed MTU and send them in order on the reliable channel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void SendToServer(byte channelId, ArraySegment segment, bool splitLargeMessages = true) + { + segment = ProcessIntermediateOutgoing(segment, true); + SetSplitValues(channelId, segment, splitLargeMessages, out int requiredSplitMessages, out int maxSplitMessageSize); + SendToServer_Internal(channelId, segment, requiredSplitMessages, maxSplitMessageSize); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SendToServer_Internal(byte channelId, ArraySegment segment, int requiredSplitMessages, int maxSplitMessageSize) + { + if (channelId >= _toServerBundles.Count) + channelId = (byte)Channel.Reliable; + + if (requiredSplitMessages > 1) + SendSplitData(null, ref segment, requiredSplitMessages, maxSplitMessageSize); + else + _toServerBundles[channelId].Write(segment); + } + + #region Splitting. + /// + /// Checks if a message can be split and outputs split information if so. + /// + private void SetSplitValues(byte channelId, ArraySegment segment, bool split, out int requiredSplitMessages, out int maxSplitMessageSize) + { + if (!split) + { + requiredSplitMessages = 0; + maxSplitMessageSize = 0; + } + else + { + SplitRequired(channelId, segment.Count, out requiredSplitMessages, out maxSplitMessageSize); + } + } + /// + /// True if data must be split. + /// + /// + /// + /// + private bool SplitRequired(byte channelId, int segmentSize, out int requiredMessages, out int maxMessageSize) + { + maxMessageSize = GetLowestMTU(channelId) - (TransportManager.TICK_BYTES + SPLIT_INDICATOR_SIZE); + requiredMessages = Mathf.CeilToInt((float)segmentSize / maxMessageSize); + + return (requiredMessages > 1); + } + + /// + /// Splits data going to which is too large to fit within the transport MTU. + /// + /// Connection to send to. If null data will be sent to the server. + /// True if data was sent split. + private void SendSplitData(NetworkConnection conn, ref ArraySegment segment, int requiredMessages, int maxMessageSize) + { + if (requiredMessages <= 1) + { + _networkManager.LogError($"SendSplitData was called with {requiredMessages} required messages. This method should only be called if messages must be split into 2 pieces or more."); + return; + } + + byte channelId = (byte)Channel.Reliable; + PooledWriter headerWriter = WriterPool.GetWriter(); + headerWriter.WritePacketId(PacketId.Split); + headerWriter.WriteInt32(requiredMessages); + ArraySegment headerSegment = headerWriter.GetArraySegment(); + + int writeIndex = 0; + bool firstWrite = true; + //Send to connection until everything is written. + while (writeIndex < segment.Count) + { + int headerReduction = 0; + if (firstWrite) + { + headerReduction = headerSegment.Count; + firstWrite = false; + } + int chunkSize = Mathf.Min(segment.Count - writeIndex - headerReduction, maxMessageSize); + //Make a new array segment for the chunk that is getting split. + ArraySegment splitSegment = new ArraySegment( + segment.Array, segment.Offset + writeIndex, chunkSize); + + //If connection is specified then it's going to a client. + if (conn != null) + { + conn.SendToClient(channelId, headerSegment, true); + conn.SendToClient(channelId, splitSegment); + } + //Otherwise it's going to the server. + else + { + _toServerBundles[channelId].Write(headerSegment, true); + _toServerBundles[channelId].Write(splitSegment, false); + } + + writeIndex += chunkSize; + } + + headerWriter.Dispose(); + } + #endregion + + + /// + /// Processes data received by the socket. + /// + /// True to process data received on the server. + internal void IterateIncoming(bool server) + { + OnIterateIncomingStart?.Invoke(server); + Transport.IterateIncoming(server); + OnIterateIncomingEnd?.Invoke(server); + } + + /// + /// Processes data to be sent by the socket. + /// + /// True to process data received on the server. + internal void IterateOutgoing(bool toServer) + { + OnIterateOutgoingStart?.Invoke(); + int channelCount = CHANNEL_COUNT; + ulong sentBytes = 0; +#if UNITY_EDITOR || DEVELOPMENT_BUILD + bool latencySimulatorEnabled = LatencySimulator.CanSimulate; +#endif + /* If sending to the client. */ + if (!toServer) + { + TimeManager tm = _networkManager.TimeManager; + uint localTick = tm.LocalTick; + //Write any dirty syncTypes. + _networkManager.ServerManager.Objects.WriteDirtySyncTypes(); + + int dirtyCount = _dirtyToClients.Count; + + //Run through all dirty connections to send data to. + for (int z = 0; z < dirtyCount; z++) + { + NetworkConnection conn = _dirtyToClients[z]; + if (conn == null || !conn.IsValid) + continue; + + //Get packets for every channel. + for (byte channel = 0; channel < channelCount; channel++) + { + if (conn.GetPacketBundle(channel, out PacketBundle pb)) + { + for (int i = 0; i < pb.WrittenBuffers; i++) + { + //Length should always be more than 0 but check to be safe. + if (pb.GetBuffer(i, out ByteBuffer bb)) + { + ArraySegment segment = new ArraySegment(bb.Data, 0, bb.Length); +#if UNITY_EDITOR || DEVELOPMENT_BUILD + if (latencySimulatorEnabled) + _latencySimulator.AddOutgoing(channel, segment, false, conn.ClientId); + else +#endif + Transport.SendToClient(channel, segment, conn.ClientId); + sentBytes += (ulong)segment.Count; + } + } + + pb.Reset(); + } + } + + /* When marked as disconnecting data will still be sent + * this iteration but the connection will be marked as invalid. + * This will prevent future data from going out/coming in. + * Also the connection will be added to a disconnecting collection + * so it will it disconnected briefly later to allow data from + * this tick to send. */ + if (conn.Disconnecting) + { + uint requiredTicks = tm.TimeToTicks(0.1d, TickRounding.RoundUp); + /* Require 100ms or 2 ticks to pass + * before disconnecting to allow for the + * higher chance of success that remaining + * data is sent. */ + requiredTicks = Math.Max(requiredTicks, 2); + _disconnectingClients.Add(new DisconnectingClient(requiredTicks + localTick, conn)); + } + + conn.ResetServerDirty(); + } + + //Iterate disconnects. + for (int i = 0; i < _disconnectingClients.Count; i++) + { + DisconnectingClient dc = _disconnectingClients[i]; + if (localTick >= dc.Tick) + { + _networkManager.TransportManager.Transport.StopConnection(dc.Connection.ClientId, true); + _disconnectingClients.RemoveAt(i); + i--; + } + } + + _networkManager.StatisticsManager.NetworkTraffic.LocalServerSentData(sentBytes); + + if (dirtyCount == _dirtyToClients.Count) + _dirtyToClients.Clear(); + else if (dirtyCount > 0) + _dirtyToClients.RemoveRange(0, dirtyCount); + } + /* If sending to the server. */ + else + { + for (byte channel = 0; channel < channelCount; channel++) + { + if (PacketBundle.GetPacketBundle(channel, _toServerBundles, out PacketBundle pb)) + { + for (int i = 0; i < pb.WrittenBuffers; i++) + { + if (pb.GetBuffer(i, out ByteBuffer bb)) + { + ArraySegment segment = new ArraySegment(bb.Data, 0, bb.Length); +#if UNITY_EDITOR || DEVELOPMENT_BUILD + if (latencySimulatorEnabled) + _latencySimulator.AddOutgoing(channel, segment); + else +#endif + Transport.SendToServer(channel, segment); + sentBytes += (ulong)segment.Count; + } + } + pb.Reset(); + } + } + + _networkManager.StatisticsManager.NetworkTraffic.LocalClientSentData(sentBytes); + } + +#if UNITY_EDITOR || DEVELOPMENT_BUILD + if (latencySimulatorEnabled) + _latencySimulator.IterateOutgoing(toServer); +#endif + + Transport.IterateOutgoing(toServer); + OnIterateOutgoingEnd?.Invoke(); + } + + #region Editor. +#if UNITY_EDITOR + private void OnValidate() + { + if (Transport == null) + Transport = GetComponent(); + + /* Update enabled state to force a reset if needed. + * This may be required if the user checked the enabled + * tick box at runtime. If enabled value didn't change + * then the Get will be the same as the Set and nothing + * will happen. */ + _latencySimulator.SetEnabled(_latencySimulator.GetEnabled()); + } +#endif + #endregion + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.cs.meta b/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.cs.meta new file mode 100644 index 0000000..b3b9a63 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 34e4a322dca349547989b14021da4e23 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Utility.meta b/Assets/FishNet/Runtime/Managing/Utility.meta new file mode 100644 index 0000000..bb492a2 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Utility.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3be43175b2896f44194e0f4ef13a9ac5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Utility/Utility.cs b/Assets/FishNet/Runtime/Managing/Utility/Utility.cs new file mode 100644 index 0000000..71774dc --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Utility/Utility.cs @@ -0,0 +1,63 @@ +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Transporting; +using UnityEngine; + +namespace FishNet.Managing.Utility +{ + + public class Packets + { + /// + /// Returns written data length for packet. + /// + internal static int GetPacketLength(ushort packetId, PooledReader reader, Channel channel) + { + /* Broadcast is a special circumstance where data + * will not be purged even if unreliable. + * This is because a broadcast receiver may not + * be set, which could be intentional. Because of this + * length is always sent to skip + * past the broadcast data. + * + * Reliables also need length read in the instance a client + * sends data to an object which server is despawning. Without + * parsing length the remainer data from client will be corrupt. */ + PacketId pid = (PacketId)packetId; + if (channel == Channel.Reliable || + pid == PacketId.Broadcast || + pid == PacketId.SyncVar + ) + { + return reader.ReadInt32(); + } + //Unreliable purges remaining. + else if (channel == Channel.Unreliable) + { + return (int)MissingObjectPacketLength.PurgeRemaiming; + } + /* Unhandled. This shouldn't be possible + * since both reliable and unreliable is checked. + * There are no other options. This is merely here + * for a sanity check. */ + else + { + LogError($"Operation is unhandled for packetId {(PacketId)packetId} on channel {channel}."); + return (int)MissingObjectPacketLength.PurgeRemaiming; + } + + //Logs an error message. + void LogError(string message) + { + if (reader.NetworkManager != null) + reader.NetworkManager.LogError(message); + else + NetworkManager.StaticLogError(message); + } + + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Utility/Utility.cs.meta b/Assets/FishNet/Runtime/Managing/Utility/Utility.cs.meta new file mode 100644 index 0000000..a2a301b --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Utility/Utility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: facab6859c82ccd49b2d1c6b80404726 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object.meta b/Assets/FishNet/Runtime/Object.meta new file mode 100644 index 0000000..194abf7 --- /dev/null +++ b/Assets/FishNet/Runtime/Object.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bcd52ca5352336e44acf3536245b6205 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Attributes.cs b/Assets/FishNet/Runtime/Object/Attributes.cs new file mode 100644 index 0000000..7742d27 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Attributes.cs @@ -0,0 +1,170 @@ +using FishNet.Managing.Logging; +using FishNet.Transporting; +using System; +using UnityEngine; + +namespace FishNet.Object +{ + + /// + /// ServerRpc methods will send messages to the server. + /// + [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)] + public class ServerRpcAttribute : Attribute + { + /// + /// True to only allow the owning client to call this RPC. + /// + public bool RequireOwnership = true; + /// + /// True to also run the RPC logic locally. + /// + public bool RunLocally = false; + /// + /// Estimated length of data being sent. + /// When a value other than -1 the minimum length of the used serializer will be this value. + /// This is useful for writing large packets which otherwise resize the serializer. + /// + public int DataLength = -1; + } + + /// + /// ObserversRpc methods will send messages to all observers. + /// + [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)] + public class ObserversRpcAttribute : Attribute + { + /// + /// True to exclude the owner from receiving this RPC. + /// + public bool ExcludeOwner = false; + /// + /// True to prevent the connection from receiving this Rpc if they are also server. + /// + public bool ExcludeServer = false; + /// + /// True to buffer the last value and send it to new players when the object is spawned for them. + /// RPC will be sent on the same channel as the original RPC, and immediately before the OnSpawnServer override. + /// + public bool BufferLast = false; + /// + /// True to also run the RPC logic locally. + /// + public bool RunLocally = false; + /// + /// Estimated length of data being sent. + /// When a value other than -1 the minimum length of the used serializer will be this value. + /// This is useful for writing large packets which otherwise resize the serializer. + /// + public int DataLength = -1; + } + + /// + /// TargetRpc methods will send messages to a single client. + /// + [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)] + public class TargetRpcAttribute : Attribute + { + /// + /// True to prevent the connection from receiving this Rpc if they are also server. + /// + public bool ExcludeServer = false; + /// + /// True to also run the RPC logic locally. + /// + public bool RunLocally = false; + /// + /// True to validate the target is possible and output debug when not. + /// Use this field with caution as it may create undesired results when set to false. + /// + public bool ValidateTarget = true; + /// + /// Estimated length of data being sent. + /// When a value other than -1 the minimum length of the used serializer will be this value. + /// This is useful for writing large packets which otherwise resize the serializer. + /// + public int DataLength = -1; + } + + /// + /// Prevents a method from running if server is not active. + /// Can only be used inside a NetworkBehaviour + /// + [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)] + public class ServerAttribute : Attribute + { + /// + /// Type of logging to use when the IsServer check fails. + /// + public LoggingType Logging = LoggingType.Warning; + } + + /// + /// Prevents this method from running if client is not active. + /// + [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)] + public class ClientAttribute : Attribute + { + /// + /// Type of logging to use when the IsClient check fails. + /// + public LoggingType Logging = LoggingType.Warning; + /// + /// True to only allow a client to run the method if they are owner of the object. + /// + public bool RequireOwnership = false; + } +} + + +namespace FishNet.Object.Synchronizing +{ + + /// + /// Synchronizes collections or objects from the server to clients. Can be used with custom SyncObjects. + /// Value must be changed on server. + /// + [AttributeUsage(AttributeTargets.Field, Inherited = true, AllowMultiple = false)] + public class SyncObjectAttribute : PropertyAttribute + { + /// + /// How often values may update over the network. + /// + public float SendRate = 0.1f; + /// + /// Clients which may receive value updates. + /// + public ReadPermission ReadPermissions = ReadPermission.Observers; + /// + /// True if to require the readonly attribute. + /// Setting to false will allow inspector serialization of this object, but you must never manually initialize this object. + /// + public bool RequireReadOnly = true; + } + + /// + /// Synchronizes a variable from server to clients automatically. + /// Value must be changed on server. + /// + [AttributeUsage(AttributeTargets.Field, Inherited = true, AllowMultiple = false)] + public class SyncVarAttribute : PropertyAttribute + { + /// + /// How often values may update over the network. + /// + public float SendRate = 0.1f; + /// + /// Clients which may receive value updates. + /// + public ReadPermission ReadPermissions = ReadPermission.Observers; + /// + /// Channel to use. Unreliable SyncVars will use eventual consistency. + /// + public Channel Channel; + /// + /// Method which will be called on the server and clients when the value changes. + /// + public string OnChange; + } + +} diff --git a/Assets/FishNet/Runtime/Object/Attributes.cs.meta b/Assets/FishNet/Runtime/Object/Attributes.cs.meta new file mode 100644 index 0000000..9bf2107 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Attributes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e2c79ec60813585469c43b4539e3d0c5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/ChangedTransformProperties.cs b/Assets/FishNet/Runtime/Object/ChangedTransformProperties.cs new file mode 100644 index 0000000..400c957 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/ChangedTransformProperties.cs @@ -0,0 +1,35 @@ +using FishNet.Documenting; + +namespace FishNet.Object +{ + /// + /// Properties which have changed on a transform. + /// + [System.Flags] + [APIExclude] + internal enum ChangedTransformProperties : byte + { + Unset = 0, + LocalPosition = 1, + LocalRotation = 2, + LocalScale = 4, + } + + [APIExclude] + internal static partial class ChangedTransformPropertiesEnum + { + /// + /// Returns if whole contains part. + /// + /// + /// + /// + public static bool Contains(ChangedTransformProperties whole, ChangedTransformProperties part) + { + return (whole & part) == part; + } + } + + +} + diff --git a/Assets/FishNet/Runtime/Object/ChangedTransformProperties.cs.meta b/Assets/FishNet/Runtime/Object/ChangedTransformProperties.cs.meta new file mode 100644 index 0000000..3357abe --- /dev/null +++ b/Assets/FishNet/Runtime/Object/ChangedTransformProperties.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1870202c019b99348aaedbe2029caf33 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Delegates.cs b/Assets/FishNet/Runtime/Object/Delegates.cs new file mode 100644 index 0000000..dab149f --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Delegates.cs @@ -0,0 +1,13 @@ +using FishNet.Connection; +using FishNet.Serializing; +using FishNet.Transporting; +using FishNet.Utility.Constant; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo(UtilityConstants.CODEGEN_ASSEMBLY_NAME)] +namespace FishNet.Object.Delegating +{ + public delegate void ServerRpcDelegate(PooledReader reader, Channel channel, NetworkConnection sender); + public delegate void ClientRpcDelegate(PooledReader reader, Channel channel); + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Delegates.cs.meta b/Assets/FishNet/Runtime/Object/Delegates.cs.meta new file mode 100644 index 0000000..67c5bde --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Delegates.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5dbd9cdda4843f34ab416273d80f83c8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/DespawnType.cs b/Assets/FishNet/Runtime/Object/DespawnType.cs new file mode 100644 index 0000000..5fa5426 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/DespawnType.cs @@ -0,0 +1,13 @@ +using FishNet.Object.Helping; + +namespace FishNet.Object +{ + + public enum DespawnType : byte + { + Destroy = 0, + Pool = 1, + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/DespawnType.cs.meta b/Assets/FishNet/Runtime/Object/DespawnType.cs.meta new file mode 100644 index 0000000..0a7a9f4 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/DespawnType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 205c3ddfc86fbaa44aa0b92ecef58474 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/EmptyNetworkBehaviour.cs b/Assets/FishNet/Runtime/Object/EmptyNetworkBehaviour.cs new file mode 100644 index 0000000..19aed6a --- /dev/null +++ b/Assets/FishNet/Runtime/Object/EmptyNetworkBehaviour.cs @@ -0,0 +1,13 @@ + +namespace FishNet.Object +{ + /// + /// This may be added at runtime to find objects without any network scripts, beneath a NetworkObject. + /// + public class EmptyNetworkBehaviour : NetworkBehaviour + { + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/EmptyNetworkBehaviour.cs.meta b/Assets/FishNet/Runtime/Object/EmptyNetworkBehaviour.cs.meta new file mode 100644 index 0000000..4c947e7 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/EmptyNetworkBehaviour.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8a6a39c46bf52104ba8efe3100bce3f7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Helping.meta b/Assets/FishNet/Runtime/Object/Helping.meta new file mode 100644 index 0000000..b4e5fab --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Helping.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0684bc52d23dfb743a5d2fab1278d8f7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Helping/Hashing.cs b/Assets/FishNet/Runtime/Object/Helping/Hashing.cs new file mode 100644 index 0000000..1f06b4e --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Helping/Hashing.cs @@ -0,0 +1,153 @@ +namespace FishNet.Object.Helping +{ + + public static class Hashing + { + + private const uint FNV_offset_basis32 = 2166136261; + private const uint FNV_prime32 = 16777619; + private const ulong FNV_offset_basis64 = 14695981039346656037; + private const ulong FNV_prime64 = 1099511628211; + + + /// + /// non cryptographic stable hash code, + /// it will always return the same hash for the same + /// string. + /// + /// This is simply an implementation of FNV-1 32 bit xor folded to 16 bit + /// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function + /// + /// The stable hash32. + /// Text. + internal static ushort GetStableHash16(this string txt) + { + uint hash32 = txt.GetStableHash32(); + + return (ushort)((hash32 >> 16) ^ hash32); + } + + + /// + /// non cryptographic stable hash code, + /// it will always return the same hash for the same + /// string. + /// + /// This is simply an implementation of FNV-1 32 bit + /// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function + /// + /// The stable hash32. + /// Text. + public static uint GetStableHash32(this string txt) + { + unchecked + { + uint hash = FNV_offset_basis32; + for (int i = 0; i < txt.Length; i++) + { + uint ch = txt[i]; + hash = hash * FNV_prime32; + hash = hash ^ ch; + } + return hash; + } + } + + /// + /// non cryptographic stable hash code, + /// it will always return the same hash for the same + /// string. + /// + /// This is simply an implementation of FNV-1 64 bit + /// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function + /// + /// The stable hash32. + /// Text. + internal static ulong GetStableHash64(this string txt) + { + unchecked + { + ulong hash = FNV_offset_basis64; + for (int i = 0; i < txt.Length; i++) + { + ulong ch = txt[i]; + hash = hash * FNV_prime64; + hash = hash ^ ch; + } + return hash; + } + } + + ///// + ///// non cryptographic stable hash code, + ///// it will always return the same hash for the same + ///// string. + ///// + ///// This is simply an implementation of FNV-1 32 bit xor folded to 16 bit + ///// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function + ///// + ///// The stable hash32. + ///// Text. + //internal static ushort GetStableHash16(this byte[] bytes) + //{ + // uint hash32 = bytes.GetStableHash32(); + + // return (ushort)((hash32 >> 16) ^ hash32); + //} + + ///// + ///// non cryptographic stable hash code, + ///// it will always return the same hash for the same + ///// string. + ///// + ///// This is simply an implementation of FNV-1 32 bit + ///// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function + ///// + ///// The stable hash32. + ///// Text. + //internal static uint GetStableHash32(this byte[] bytes) + //{ + // unchecked + // { + // uint hash = FNV_offset_basis32; + // for (int i = 0; i < bytes.Length; i++) + // { + // uint bt = bytes[i]; + // hash = hash * FNV_prime32; + // hash = hash ^ bt; + // } + // return hash; + // } + //} + + ///// + ///// non cryptographic stable hash code, + ///// it will always return the same hash for the same + ///// string. + ///// + ///// This is simply an implementation of FNV-1 64 bit + ///// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function + ///// + ///// The stable hash32. + ///// Text. + //internal static ulong GetStableHash64(this byte[] bytes) + //{ + // unchecked + // { + // ulong hash = FNV_offset_basis64; + // for (int i = 0; i < bytes.Length; i++) + // { + // ulong bt = bytes[i]; + // hash = hash * FNV_prime64; + // hash = hash ^ bt; + // } + // return hash; + // } + //} + + + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Helping/Hashing.cs.meta b/Assets/FishNet/Runtime/Object/Helping/Hashing.cs.meta new file mode 100644 index 0000000..327d971 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Helping/Hashing.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7c55dc5a22646764aa5dbfab2062669e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Helping/RpcLink.cs b/Assets/FishNet/Runtime/Object/Helping/RpcLink.cs new file mode 100644 index 0000000..53c2aea --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Helping/RpcLink.cs @@ -0,0 +1,39 @@ +using FishNet.Object.Helping; + +namespace FishNet.Object +{ + + #region Types. + /// + /// Lookup data for a RPC Link. + /// + internal struct RpcLink + { + /// + /// ObjectId for link. + /// + public int ObjectId; + /// + /// NetworkBehaviour component index on ObjectId. + /// + public byte ComponentIndex; + /// + /// RpcHash for link. + /// + public uint RpcHash; + /// + /// Type of Rpc link is for. + /// + public RpcType RpcType; + + public RpcLink(int objectId, byte componentIndex, uint rpcHash, RpcType rpcType) + { + ObjectId = objectId; + ComponentIndex = componentIndex; + RpcHash = rpcHash; + RpcType = rpcType; + } + } + #endregion + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Helping/RpcLink.cs.meta b/Assets/FishNet/Runtime/Object/Helping/RpcLink.cs.meta new file mode 100644 index 0000000..b76b723 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Helping/RpcLink.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 05a91745dd829d043aadf391ac7b233e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Helping/RpcType.cs b/Assets/FishNet/Runtime/Object/Helping/RpcType.cs new file mode 100644 index 0000000..5bf072a --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Helping/RpcType.cs @@ -0,0 +1,13 @@ +namespace FishNet.Object.Helping +{ + public enum RpcType : int + { + None = 0, + Server = 1, + Observers = 2, + Target = 4, + Replicate = 8, + Reconcile = 16 + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Helping/RpcType.cs.meta b/Assets/FishNet/Runtime/Object/Helping/RpcType.cs.meta new file mode 100644 index 0000000..7cb63bd --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Helping/RpcType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 09f9e7236f988c64fad54645f4cc7f8b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Helping/StaticShortcuts.cs b/Assets/FishNet/Runtime/Object/Helping/StaticShortcuts.cs new file mode 100644 index 0000000..32e781f --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Helping/StaticShortcuts.cs @@ -0,0 +1,49 @@ + +namespace FishNet.Object.Helping +{ + + public static class CodegenHelper + { + /// + /// Returns if a NetworkObject is deinitializing. + /// + /// + /// + public static bool NetworkObject_Deinitializing(NetworkBehaviour nb) + { + if (nb == null) + return true; + + return nb.IsDeinitializing; + } + + /// + /// Returns if running as server. + /// + /// + /// + public static bool IsServer(NetworkBehaviour nb) + { + if (nb == null) + return false; + + return nb.IsServer; + } + + /// + /// Returns if running as client. + /// + /// + /// + public static bool IsClient(NetworkBehaviour nb) + { + if (nb == null) + return false; + + return nb.IsClient; + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Helping/StaticShortcuts.cs.meta b/Assets/FishNet/Runtime/Object/Helping/StaticShortcuts.cs.meta new file mode 100644 index 0000000..70bef6e --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Helping/StaticShortcuts.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 149cde0042627604d810c2c7fc0f9176 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.Callbacks.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour.Callbacks.cs new file mode 100644 index 0000000..bf1d1fc --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.Callbacks.cs @@ -0,0 +1,184 @@ +#if UNITY_2020_3_OR_NEWER && UNITY_EDITOR_WIN +using FishNet.CodeAnalysis.Annotations; +#endif +using FishNet.Connection; +using FishNet.Documenting; +using FishNet.Object.Synchronizing.Internal; +using UnityEngine; + +namespace FishNet.Object +{ + + public abstract partial class NetworkBehaviour : MonoBehaviour + { + #region Public. + /// + /// True if OnStartServer has been called. + /// + [APIExclude] + public bool OnStartServerCalled { get; private set; } + /// + /// True if OnStartClient has been called. + /// + [APIExclude] + public bool OnStartClientCalled { get; private set; } + #endregion + + #region Private. + /// + /// True if OnStartNetwork has been called. + /// + private bool _onStartNetworkCalled; + /// + /// True if OnStopNetwork has been called. + /// + private bool _onStopNetworkCalled; + #endregion + + /// + /// Invokes cached callbacks on SyncTypes which were held until OnStartXXXXX was called. + /// + /// + internal void InvokeSyncTypeCallbacks(bool asServer) + { + foreach (SyncBase item in _syncVars.Values) + item.OnStartCallback(asServer); + foreach (SyncBase item in _syncObjects.Values) + item.OnStartCallback(asServer); + } + /// + /// Invokes the OnStart/StopNetwork. + /// + /// + internal void InvokeOnNetwork(bool start) + { + if (start) + { + if (_onStartNetworkCalled) + return; + OnStartNetwork(); + } + else + { + if (_onStopNetworkCalled) + return; + OnStopNetwork(); + } + } + + /// + /// Called when the network has initialized this object. May be called for server or client but will only be called once. + /// When as host or server this method will run before OnStartServer. + /// When as client only the method will run before OnStartClient. + /// +#if UNITY_2020_3_OR_NEWER && UNITY_EDITOR_WIN + [OverrideMustCallBase(BaseCallMustBeFirstStatement = true)] +#endif + public virtual void OnStartNetwork() + { + _onStartNetworkCalled = true; + _onStopNetworkCalled = false; + } + /// + /// Called when the network is deinitializing this object. May be called for server or client but will only be called once. + /// When as host or server this method will run after OnStopServer. + /// When as client only this method will run after OnStopClient. + /// +#if UNITY_2020_3_OR_NEWER && UNITY_EDITOR_WIN + [OverrideMustCallBase(BaseCallMustBeFirstStatement = true)] +#endif + public virtual void OnStopNetwork() + { + _onStopNetworkCalled = true; + _onStartNetworkCalled = false; + } + + /// + /// Called on the server after initializing this object. + /// SyncTypes modified before or during this method will be sent to clients in the spawn message. + /// +#if UNITY_2020_3_OR_NEWER && UNITY_EDITOR_WIN + [OverrideMustCallBase(BaseCallMustBeFirstStatement = true)] +#endif + public virtual void OnStartServer() + { + OnStartServerCalled = true; + } + /// + /// Called on the server before deinitializing this object. + /// +#if UNITY_2020_3_OR_NEWER && UNITY_EDITOR_WIN + [OverrideMustCallBase(BaseCallMustBeFirstStatement = true)] +#endif + public virtual void OnStopServer() + { + OnStartServerCalled = false; + ReturnRpcLinks(); + } + /// + /// Called on the server after ownership has changed. + /// + /// Previous owner of this object. +#if UNITY_2020_3_OR_NEWER && UNITY_EDITOR_WIN + [OverrideMustCallBase(BaseCallMustBeFirstStatement = true)] +#endif + public virtual void OnOwnershipServer(NetworkConnection prevOwner) + { + //When switching ownership always clear replicate cache on server. + ClearReplicateCache_Internal(true); + } + /// + /// Called on the server after a spawn message for this object has been sent to clients. + /// Useful for sending remote calls or data to clients. + /// + /// Connection the object is being spawned for. +#if UNITY_2020_3_OR_NEWER && UNITY_EDITOR_WIN + [OverrideMustCallBase(BaseCallMustBeFirstStatement = true)] +#endif + public virtual void OnSpawnServer(NetworkConnection connection) { } + /// + /// Called on the server before a despawn message for this object has been sent to connection. + /// Useful for sending remote calls or actions to clients. + /// +#if UNITY_2020_3_OR_NEWER && UNITY_EDITOR_WIN + [OverrideMustCallBase(BaseCallMustBeFirstStatement = true)] +#endif + public virtual void OnDespawnServer(NetworkConnection connection) { } + /// + /// Called on the client after initializing this object. + /// +#if UNITY_2020_3_OR_NEWER && UNITY_EDITOR_WIN + [OverrideMustCallBase(BaseCallMustBeFirstStatement = true)] +#endif + public virtual void OnStartClient() + { + OnStartClientCalled = true; + } + /// + /// Called on the client before deinitializing this object. + /// +#if UNITY_2020_3_OR_NEWER && UNITY_EDITOR_WIN + [OverrideMustCallBase(BaseCallMustBeFirstStatement = true)] +#endif + public virtual void OnStopClient() + { + OnStartClientCalled = false; + } + /// + /// Called on the client after gaining or losing ownership. + /// + /// Previous owner of this object. +#if UNITY_2020_3_OR_NEWER && UNITY_EDITOR_WIN + [OverrideMustCallBase(BaseCallMustBeFirstStatement = true)] +#endif + public virtual void OnOwnershipClient(NetworkConnection prevOwner) + { + //If losing or gaining ownership then clear replicate cache. + if (IsOwner || prevOwner == LocalConnection) + ClearReplicateCache_Internal(false); + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.Callbacks.cs.meta b/Assets/FishNet/Runtime/Object/NetworkBehaviour.Callbacks.cs.meta new file mode 100644 index 0000000..f5aabb3 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.Callbacks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a9ddaf08801752b49bcfe720217df74a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.Logging.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour.Logging.cs new file mode 100644 index 0000000..496e8ac --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.Logging.cs @@ -0,0 +1,22 @@ +using FishNet.Managing.Logging; +using UnityEngine; + +namespace FishNet.Object +{ + + public abstract partial class NetworkBehaviour : MonoBehaviour + { + + /// + /// True if can log for loggingType. + /// + /// Type of logging being filtered. + /// + public bool CanLog(LoggingType loggingType) + { + return (NetworkManager == null) ? false : NetworkManager.CanLog(loggingType); + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.Logging.cs.meta b/Assets/FishNet/Runtime/Object/NetworkBehaviour.Logging.cs.meta new file mode 100644 index 0000000..e446436 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.Logging.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ad4dd2795a9a00e4d814892ac1a67157 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.Prediction.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour.Prediction.cs new file mode 100644 index 0000000..cccee30 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.Prediction.cs @@ -0,0 +1,746 @@ +using FishNet.Connection; +using FishNet.Documenting; +using FishNet.Managing.Predicting; +using FishNet.Managing.Timing; +using FishNet.Object.Prediction; +using FishNet.Object.Prediction.Delegating; +using FishNet.Serializing; +using FishNet.Serializing.Helping; +using FishNet.Transporting; +using FishNet.Utility.Constant; +using FishNet.Utility.Extension; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; +using UnityScene = UnityEngine.SceneManagement.Scene; + +[assembly: InternalsVisibleTo(UtilityConstants.CODEGEN_ASSEMBLY_NAME)] +namespace FishNet.Object +{ + + public abstract partial class NetworkBehaviour : MonoBehaviour + { + #region Public. + /// + /// + /// + private uint _lastReconcileTick; + /// + /// Gets the last tick this NetworkBehaviour reconciled with. + /// + public uint GetLastReconcileTick() => _lastReconcileTick; + + internal void SetLastReconcileTick(uint value, bool updateGlobals = true) + { + _lastReconcileTick = value; + if (updateGlobals) + PredictionManager.LastReconcileTick = value; + } + /// + /// + /// + private uint _lastReplicateTick; + /// + /// Gets the last tick this NetworkBehaviour replicated with. + /// + public uint GetLastReplicateTick() => _lastReplicateTick; + /// + /// Sets the last tick this NetworkBehaviour replicated with. + /// For internal use only. + /// + private void SetLastReplicateTick(uint value, bool updateGlobals = true) + { + _lastReplicateTick = value; + if (updateGlobals) + { + Owner.LocalReplicateTick = TimeManager.LocalTick; + PredictionManager.LastReplicateTick = value; + } + } + /// + /// True if this object is reconciling. + /// + public bool IsReconciling { get; internal set; } + #endregion + + #region Private. + /// + /// Registered Replicate methods. + /// + private readonly Dictionary _replicateRpcDelegates = new Dictionary(); + /// + /// Registered Reconcile methods. + /// + private readonly Dictionary _reconcileRpcDelegates = new Dictionary(); + /// + /// True if initialized compnents for prediction. + /// + private bool _predictionInitialized; + /// + /// Rigidbody found on this object. This is used for prediction. + /// + private Rigidbody _predictionRigidbody; + /// + /// Rigidbody2D found on this object. This is used for prediction. + /// + private Rigidbody2D _predictionRigidbody2d; + /// + /// Last position for TransformMayChange. + /// + private Vector3 _lastMayChangePosition; + /// + /// Last rotation for TransformMayChange. + /// + private Quaternion _lastMayChangeRotation; + /// + /// Last scale for TransformMayChange. + /// + private Vector3 _lastMayChangeScale; + /// + /// Number of resends which may occur. This could be for client resending replicates to the server or the server resending reconciles to the client. + /// + private int _remainingResends; + /// + /// Last enqueued replicate tick on the server. + /// + private uint _lastReceivedReplicateTick; + /// + /// Last tick of a reconcile received from the server. + /// + private uint _lastReceivedReconcileTick; + /// + /// True if the client has cached reconcile + /// + private bool _clientHasReconcileData; + #endregion + + /// + /// Registers a RPC method. + /// Internal use. + /// + /// + /// + [APIExclude] + [CodegenMakePublic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected internal void RegisterReplicateRpc_Internal(uint hash, ReplicateRpcDelegate del) + { + _replicateRpcDelegates[hash] = del; + } + /// + /// Registers a RPC method. + /// Internal use. + /// + /// + /// + [APIExclude] + [CodegenMakePublic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected internal void RegisterReconcileRpc_Internal(uint hash, ReconcileRpcDelegate del) + { + _reconcileRpcDelegates[hash] = del; + } + + + /// + /// Called when a replicate is received. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void OnReplicateRpc(uint? methodHash, PooledReader reader, NetworkConnection sendingClient, Channel channel) + { + if (methodHash == null) + methodHash = ReadRpcHash(reader); + + if (sendingClient == null) + { + _networkObjectCache.NetworkManager.LogError($"NetworkConnection is null. Replicate {methodHash.Value} on {gameObject.name}, behaviour {GetType().Name} will not complete. Remainder of packet may become corrupt."); + return; + } + + if (_replicateRpcDelegates.TryGetValueIL2CPP(methodHash.Value, out ReplicateRpcDelegate del)) + del.Invoke(reader, sendingClient, channel); + else + _networkObjectCache.NetworkManager.LogWarning($"Replicate not found for hash {methodHash.Value} on {gameObject.name}, behaviour {GetType().Name}. Remainder of packet may become corrupt."); + } + + + /// + /// Called when a reconcile is received. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void OnReconcileRpc(uint? methodHash, PooledReader reader, Channel channel) + { + if (methodHash == null) + methodHash = ReadRpcHash(reader); + + if (_reconcileRpcDelegates.TryGetValueIL2CPP(methodHash.Value, out ReconcileRpcDelegate del)) + del.Invoke(reader, channel); + else + _networkObjectCache.NetworkManager.LogWarning($"Reconcile not found for hash {methodHash.Value}. Remainder of packet may become corrupt."); + } + + /// + /// Clears cached replicates. This can be useful to call on server and client after teleporting. + /// + /// True to reset values for server, false to reset values for client. + public void ClearReplicateCache(bool asServer) + { + ResetLastPredictionTicks(); + ClearReplicateCache_Internal(asServer); + } + /// + /// Clears cached replicates for server and client. This can be useful to call on server and client after teleporting. + /// + public void ClearReplicateCache() + { + ResetLastPredictionTicks(); + ClearReplicateCache_Internal(true); + ClearReplicateCache_Internal(false); + } + /// + /// Resets last predirection tick values. + /// + private void ResetLastPredictionTicks() + { + _lastSentReplicateTick = 0; + _lastReceivedReplicateTick = 0; + _lastReceivedReconcileTick = 0; + SetLastReconcileTick(0, false); + SetLastReplicateTick(0, false); + } + /// + /// Clears cached replicates. + /// For internal use only. + /// + /// + [CodegenMakePublic] + [APIExclude] + protected internal virtual void ClearReplicateCache_Internal(bool asServer) { } + + /// + /// Writes number of past inputs from buffer to writer and sends it to the server. + /// Internal use. + /// + [CodegenMakePublic] + [APIExclude] + private void SendReplicateRpc(uint hash, List replicates, Channel channel) where T : IReplicateData + { + if (!IsSpawnedWithWarning()) + return; + + int bufferCount = replicates.Count; + int lastBufferIndex = (bufferCount - 1); + //Nothing to send; should never be possible. + if (lastBufferIndex < 0) + return; + + //Number of past inputs to send. + int pastInputs = Mathf.Min(PredictionManager.GetRedundancyCount(), bufferCount); + /* Where to start writing from. When passed + * into the writer values from this offset + * and forward will be written. */ + int offset = bufferCount - pastInputs; + if (offset < 0) + offset = 0; + + uint lastReplicateTick = _lastSentReplicateTick; + if (lastReplicateTick > 0) + { + uint diff = TimeManager.LocalTick - GetLastReplicateTick(); + offset += (int)diff - 1; + if (offset >= replicates.Count) + return; + } + + _lastSentReplicateTick = TimeManager.LocalTick; + + //Write history to methodWriter. + PooledWriter methodWriter = WriterPool.GetWriter(WriterPool.LENGTH_BRACKET); + methodWriter.WriteReplicate(replicates, offset); + PooledWriter writer; + //if (_rpcLinks.TryGetValueIL2CPP(hash, out RpcLinkType link)) + //writer = CreateLinkedRpc(link, methodWriter, Channel.Unreliable); + //else //todo add support for -> server rpc links. + + writer = CreateRpc(hash, methodWriter, PacketId.Replicate, channel); + NetworkManager.TransportManager.SendToServer((byte)channel, writer.GetArraySegment(), false); + + /* If being sent as reliable then clear buffer + * since we know it will get there. + * Also reset remaining resends. */ + if (channel == Channel.Reliable) + { + replicates.Clear(); + _remainingResends = 0; + } + + methodWriter.DisposeLength(); + writer.DisposeLength(); + } + + private uint _lastSentReplicateTick; + + /// + /// Sends a RPC to target. + /// Internal use. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CodegenMakePublic] + [APIExclude] + internal void SendReconcileRpc(uint hash, T reconcileData, Channel channel) + { + if (!IsSpawned) + return; + if (!Owner.IsActive) + return; + + PooledWriter methodWriter = WriterPool.GetWriter(); + methodWriter.WriteUInt32(GetLastReplicateTick()); + methodWriter.Write(reconcileData); + + PooledWriter writer; +#if UNITY_EDITOR || DEVELOPMENT_BUILD + if (NetworkManager.DebugManager.ReconcileRpcLinks && _rpcLinks.TryGetValueIL2CPP(hash, out RpcLinkType link)) +#else + if (_rpcLinks.TryGetValueIL2CPP(hash, out RpcLinkType link)) +#endif + writer = CreateLinkedRpc(link, methodWriter, channel); + else + writer = CreateRpc(hash, methodWriter, PacketId.Reconcile, channel); + + _networkObjectCache.NetworkManager.TransportManager.SendToClient((byte)channel, writer.GetArraySegment(), Owner); + + methodWriter.Dispose(); + writer.Dispose(); + } + + /// + /// Returns if there is a chance the transform may change after the tick. + /// + /// + protected internal bool TransformMayChange() + { + if (!_predictionInitialized) + { + _predictionInitialized = true; + _predictionRigidbody = GetComponentInParent(); + _predictionRigidbody2d = GetComponentInParent(); + } + + /* Use distance when checking if changed because rigidbodies can twitch + * or move an extremely small amount. These small moves are not worth + * resending over because they often fix themselves each frame. */ + float changeDistance = 0.000004f; + + bool positionChanged = (transform.position - _lastMayChangePosition).sqrMagnitude > changeDistance; + bool rotationChanged = (transform.rotation.eulerAngles - _lastMayChangeRotation.eulerAngles).sqrMagnitude > changeDistance; + bool scaleChanged = (transform.localScale - _lastMayChangeScale).sqrMagnitude > changeDistance; + bool transformChanged = (positionChanged || rotationChanged || scaleChanged); + /* Returns true if transform.hasChanged, or if either + * of the rigidbodies have velocity. */ + bool changed = ( + transformChanged || + (_predictionRigidbody != null && (_predictionRigidbody.velocity != Vector3.zero || _predictionRigidbody.angularVelocity != Vector3.zero)) || + (_predictionRigidbody2d != null && (_predictionRigidbody2d.velocity != Vector2.zero || _predictionRigidbody2d.angularVelocity != 0f)) + ); + + //If transform changed update last values. + if (transformChanged) + { + _lastMayChangePosition = transform.position; + _lastMayChangeRotation = transform.rotation; + _lastMayChangeScale = transform.localScale; + } + + return changed; + } + + /// + /// Checks conditions for a replicate. + /// + /// True if checking as server. + /// Returns true if to exit the replicate early. + [CodegenMakePublic] //internal + [APIExclude] + public bool Replicate_ExitEarly_A_Internal(bool asServer, bool replaying) + { + bool isOwner = IsOwner; + //Server. + if (asServer) + { + //No owner, do not try to replicate 'owner' input. + if (!Owner.IsActive) + { + ClearReplicateCache(true); + return true; + } + //Is client host, no need to use CSP; trust client. + if (isOwner) + { + ClearReplicateCache(); + return true; + } + } + //Client. + else + { + //Server does not replay; this should never happen. + if (replaying && IsServer) + return true; + //Spectators cannot replicate. + if (!isOwner) + { + ClearReplicateCache(false); + return true; + } + } + + //Checks pass. + return false; + } + + /// + /// Clears a Queue. + /// This is used as a patch for Unity 2022 Burst compiler bugs. + /// Using Queue.Clear via codegen throws for an unknown reason. + /// + public void ClearQueue_Server_Internal(Queue q) + { + q.Clear(); + } + + /// + /// Gets the next replicate in queue. + /// + [CodegenMakePublic] //internal + [APIExclude] + public void Replicate_Server_Internal(ReplicateUserLogicDelegate del, Queue q, Channel channel) where T : IReplicateData + { + int count = q.Count; + if (count > 0) + { + ReplicateData(q.Dequeue()); + count--; + + PredictionManager pm = PredictionManager; + bool consumeExcess = !pm.DropExcessiveReplicates; + //Number of entries to leave in buffer when consuming. + const int leaveInBuffer = 2; + //Only consume if the queue count is over leaveInBuffer. + if (consumeExcess && count > leaveInBuffer) + { + byte maximumAllowedConsumes = pm.GetMaximumConsumeCount(); + int maximumPossibleConsumes = (count - leaveInBuffer); + int consumeAmount = Mathf.Min(maximumAllowedConsumes, maximumPossibleConsumes); + + for (int i = 0; i < consumeAmount; i++) + ReplicateData(q.Dequeue()); + } + + void ReplicateData(T data) + { + uint tick = data.GetTick(); + SetLastReplicateTick(tick); + del.Invoke(data, true, channel, false); + } + + _remainingResends = pm.GetRedundancyCount(); + } + else + { + del.Invoke(default, true, channel, false); + } + } + + /// + /// Returns the maximum amount of replicates when consuming multiple inputs per tick. + /// + /// + private int GetMaximumReplicatesWhenConsuming() => (TimeManager.TickRate * 3); + + /// + /// Returns if a replicates data changed and updates resends as well data tick. + /// + /// True to enqueue data for replaying. + /// True if data has changed.. + [CodegenMakePublic] //internal + [APIExclude] + public void Replicate_Client_Internal(ReplicateUserLogicDelegate del, uint methodHash, List replicates, T data, Channel channel) where T : IReplicateData + { + //Only check to enqueu/send if not clientHost. + if (!IsServer) + { + Func isDefaultDel = GeneratedComparer.IsDefault; + if (isDefaultDel == null) + { + NetworkManager.LogError($"ReplicateComparers not found for type {typeof(T).FullName}"); + return; + } + + //If there's no datas then reset last replicate send tick. + if (replicates.Count == 0) + _lastSentReplicateTick = 0; + + PredictionManager pm = NetworkManager.PredictionManager; + + bool isDefault = isDefaultDel.Invoke(data); + bool mayChange = TransformMayChange(); + bool resetResends = (pm.UsingRigidbodies || mayChange || !isDefault); + /* If there is going to be a resend then enqueue data no matter what. + * Then ensures there are no data gaps for ticks. EG, input may + * look like this... + * Move - tick 0. + * Idle - tick 1. + * Move - tick 2. + * + * If there were no 'using rigidbodies' then resetResends may be false. + * As result the queue would be filled like this... + * Move - tick 0. + * Move - tick 2. + * + * The ticks are not sent per data, just once and incremented once per data. + * Due to this the results would actually be... + * Move - tick 0. + * Move - tick 1 (should be tick 2!). + * + * But by including data if there will be resends the defaults will become added. */ + if (resetResends) + _remainingResends = pm.GetRedundancyCount(); + + bool enqueueData = (_remainingResends > 0); + if (enqueueData) + { + /* Replicates will be limited to 1 second + * worth on the client. That means the client + * will only lose replays if they do not receive + * a response back from the server for over a second. + * When a client drops a replay it does not necessarily mean + * they will be out of synchronization, but rather they + * will not be able to reconcile that tick. */ + /* Even though limit is 1 second only remove entries if over 2 seconds + * to prevent constant remove calls to the collection. */ + int maximumReplicates = (TimeManager.TickRate * 2); + //If over then remove half the replicates. + if (replicates.Count >= maximumReplicates) + { + int removeCount = (maximumReplicates / 2); + //Dispose first. + for (int i = 0; i < removeCount; i++) + replicates[i].Dispose(); + //Then remove. + replicates.RemoveRange(0, removeCount); + } + + uint localTick = TimeManager.LocalTick; + //Update tick on the data to current. + data.SetTick(localTick); + //Add to collection. + replicates.Add(data); + } + + //If theres resends left. + if (_remainingResends > 0) + { + _remainingResends--; + SendReplicateRpc(methodHash, replicates, channel); + //Update last replicate tick. + SetLastReplicateTick(TimeManager.LocalTick); + } + } + + del.Invoke(data, false, channel, false); + } + + + /// + /// Reads a replicate the client. + /// + public void Replicate_Reader_Internal(PooledReader reader, NetworkConnection sender, T[] arrBuffer, Queue replicates, Channel channel) where T : IReplicateData + { + int receivedReplicatesCount = reader.ReadReplicate(ref arrBuffer, TimeManager.LastPacketTick); + if (!OwnerMatches(sender)) + return; + + PredictionManager pm = PredictionManager; + bool consumeExcess = !pm.DropExcessiveReplicates; + //Maximum number of replicates allowed to be queued at once. + int replicatesCountLimit = (consumeExcess) ? + GetMaximumReplicatesWhenConsuming() : pm.GetMaximumServerReplicates(); + + for (int i = 0; i < receivedReplicatesCount; i++) + { + uint tick = arrBuffer[i].GetTick(); + if (tick > _lastReceivedReplicateTick) + { + //Cannot queue anymore, discard oldest. + if (replicates.Count >= replicatesCountLimit) + { + T data = replicates.Dequeue(); + data.Dispose(); + } + + replicates.Enqueue(arrBuffer[i]); + _lastReceivedReplicateTick = tick; + } + } + } + + /// + /// Checks conditions for a reconcile. + /// + /// True if checking as server. + /// Returns true if able to continue. + [CodegenMakePublic] //internal + [APIExclude] + public bool Reconcile_ExitEarly_A_Internal(bool asServer, out Channel channel) + { + channel = Channel.Unreliable; + //Server. + if (asServer) + { + if (_remainingResends <= 0) + return true; + + _remainingResends--; + if (_remainingResends == 0) + channel = Channel.Reliable; + } + //Client. + else + { + if (!_clientHasReconcileData) + return true; + + _clientHasReconcileData = false; + /* If clientHost then invoke reconciles but + * don't actually reconcile. This is done + * because certain user code may + * rely on those events running even as host. */ + if (IsServer) + { + PredictionManager.InvokeOnReconcile_Internal(this, true); + PredictionManager.InvokeOnReconcile_Internal(this, false); + return true; + } + } + + //Checks pass. + return false; + } + + /// + /// Updates lastReconcileTick as though running asServer. + /// + /// Data to set tick on. + public void Reconcile_Server_Internal(uint methodHash, T data, Channel channel) where T : IReconcileData + { + //Server always uses last replicate tick as reconcile tick. + uint tick = _lastReplicateTick; + data.SetTick(tick); + SetLastReconcileTick(tick); + + PredictionManager.InvokeServerReconcile(this, true); + SendReconcileRpc(methodHash, data, channel); + PredictionManager.InvokeServerReconcile(this, false); + + } + + /// + /// Processes a reconcile for client. + /// + public void Reconcile_Client_Internal(ReconcileUserLogicDelegate reconcileDel, ReplicateUserLogicDelegate replicateULDel, List replicates, T data, Channel channel) where T : IReconcileData where T2 : IReplicateData + { + uint tick = data.GetTick(); + + /* If the first entry in cllection has a tick higher than + * the received tick then something went wrong, do not reconcile. */ + if (replicates.Count > 0 && replicates[0].GetTick() > tick) + return; + + UnityScene scene = gameObject.scene; + PhysicsScene ps = scene.GetPhysicsScene(); + PhysicsScene2D ps2d = scene.GetPhysicsScene2D(); + + //This must be set before reconcile is invoked. + SetLastReconcileTick(tick); + //Invoke that reconcile is starting. + PredictionManager.InvokeOnReconcile_Internal(this, true); + //Call reconcile user logic. + reconcileDel?.Invoke(data, false, channel); + + //True if the timemanager is handling physics simulations. + bool tmPhysics = (TimeManager.PhysicsMode == PhysicsMode.TimeManager); + //Sync transforms if using tm physics. + if (tmPhysics) + { + Physics.SyncTransforms(); + Physics2D.SyncTransforms(); + } + + //Remove excess from buffered inputs. + int queueIndex = -1; + for (int i = 0; i < replicates.Count; i++) + { + if (replicates[i].GetTick() == tick) + { + queueIndex = i; + break; + } + } + //Now found, weird. + if (queueIndex == -1) + replicates.Clear(); + //Remove up to found, including it. + else + replicates.RemoveRange(0, queueIndex + 1); + + //Number of replays which will be performed. + int replays = replicates.Count; + float tickDelta = (float)TimeManager.TickDelta; + + for (int i = 0; i < replays; i++) + { + T2 rData = replicates[i]; + uint replayTick = rData.GetTick(); + + PredictionManager.InvokeOnReplicateReplay_Internal(scene, replayTick, ps, ps2d, true); + + //Replay the data using the replicate logic delegate. + replicateULDel.Invoke(rData, false, channel, true); + if (tmPhysics) + { + ps.Simulate(tickDelta); + ps2d.Simulate(tickDelta); + } + + PredictionManager.InvokeOnReplicateReplay_Internal(scene, replayTick, ps, ps2d, false); + } + + //Reconcile ended. + PredictionManager.InvokeOnReconcile_Internal(this, false); + } + + /// + /// Reads a reconcile the client. + /// + public void Reconcile_Reader_Internal(PooledReader reader, ref T data, Channel channel) where T : IReconcileData + { + uint tick = reader.ReadUInt32(); + T newData = reader.Read(); + + //Tick is old or already processed. + if (tick <= _lastReceivedReconcileTick) + return; + //Only owner reconciles. Maybe ownership changed then packet arrived out of order. + if (!IsOwner) + return; + + data = newData; + data.SetTick(tick); + _clientHasReconcileData = true; + _lastReceivedReconcileTick = tick; + } + + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.Prediction.cs.meta b/Assets/FishNet/Runtime/Object/NetworkBehaviour.Prediction.cs.meta new file mode 100644 index 0000000..1d59a32 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.Prediction.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 074dac4dd3f9f6a4d8c1eb1191334472 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.QOL.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour.QOL.cs new file mode 100644 index 0000000..e4da3a7 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.QOL.cs @@ -0,0 +1,258 @@ +#if UNITY_2020_3_OR_NEWER && UNITY_EDITOR_WIN +using FishNet.CodeAnalysis.Annotations; +#endif +using FishNet.Component.ColliderRollback; +using FishNet.Connection; +using FishNet.Managing; +using FishNet.Managing.Client; +using FishNet.Managing.Observing; +using FishNet.Managing.Predicting; +using FishNet.Managing.Scened; +using FishNet.Managing.Server; +using FishNet.Managing.Timing; +using FishNet.Managing.Transporting; +using FishNet.Observing; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace FishNet.Object +{ + + public abstract partial class NetworkBehaviour : MonoBehaviour + { + /// + /// True if the NetworkObject for this NetworkBehaviour is deinitializing. + /// + public bool IsDeinitializing => _networkObjectCache.IsDeinitializing; + /// + /// NetworkManager for this object. + /// + public NetworkManager NetworkManager => _networkObjectCache.NetworkManager; + /// + /// ServerManager for this object. + /// + public ServerManager ServerManager => _networkObjectCache.ServerManager; + /// + /// ClientManager for this object. + /// + public ClientManager ClientManager => _networkObjectCache.ClientManager; + /// + /// ObserverManager for this object. + /// + public ObserverManager ObserverManager => _networkObjectCache.ObserverManager; + /// + /// TransportManager for this object. + /// + public TransportManager TransportManager => _networkObjectCache.TransportManager; + /// + /// TimeManager for this object. + /// + public TimeManager TimeManager => _networkObjectCache.TimeManager; + /// + /// SceneManager for this object. + /// + public SceneManager SceneManager => _networkObjectCache.SceneManager; + /// + /// PredictionManager for this object. + /// + public PredictionManager PredictionManager => _networkObjectCache.PredictionManager; + /// + /// RollbackManager for this object. + /// + public RollbackManager RollbackManager => _networkObjectCache.RollbackManager; + /// + /// NetworkObserver on this object. + /// + public NetworkObserver NetworkObserver => _networkObjectCache.NetworkObserver; + /// + /// True if the client is active and authenticated. + /// + public bool IsClient => _networkObjectCache.IsClient; + /// + /// True if only the client is active and authenticated. + /// + public bool IsClientOnly => _networkObjectCache.IsClientOnly; + /// + /// True if server is active. + /// + public bool IsServer => _networkObjectCache.IsServer; + /// + /// True if only the server is active. + /// + public bool IsServerOnly => _networkObjectCache.IsServerOnly; + /// + /// True if client and server are active. + /// + public bool IsHost => _networkObjectCache.IsHost; + /// + /// True if client nor server are active. + /// + public bool IsOffline => _networkObjectCache.IsOffline; + /// + /// Observers for this NetworkBehaviour. + /// + public HashSet Observers => _networkObjectCache.Observers; + /// + /// True if the local client is the owner of this object. + /// +#if UNITY_2020_3_OR_NEWER && UNITY_EDITOR_WIN + [PreventUsageInside("global::FishNet.Object.NetworkBehaviour", "OnStartServer", "")] + [PreventUsageInside("global::FishNet.Object.NetworkBehaviour", "OnStartNetwork", " Use base.Owner.IsLocalClient instead.")] + [PreventUsageInside("global::FishNet.Object.NetworkBehaviour", "Awake", "")] + [PreventUsageInside("global::FishNet.Object.NetworkBehaviour", "Start", "")] +#endif + public bool IsOwner => _networkObjectCache.IsOwner; + /// + /// Owner of this object. + /// + public NetworkConnection Owner + { + get + { + //Ensures a null Owner is never returned. + if (_networkObjectCache == null) + return FishNet.Managing.NetworkManager.EmptyConnection; + + return _networkObjectCache.Owner; + } + } + /// + /// ClientId for this NetworkObject owner. + /// + public int OwnerId => _networkObjectCache.OwnerId; + /// + /// Unique Id for this _networkObjectCache. This does not represent the object owner. + /// + public int ObjectId => _networkObjectCache.ObjectId; + /// + /// The local connection of the client calling this method. + /// + public NetworkConnection LocalConnection => _networkObjectCache.LocalConnection; + /// + /// Returns if a connection is the owner of this object. + /// + /// + /// + public bool OwnerMatches(NetworkConnection connection) + { + return (_networkObjectCache.Owner == connection); + } + + /// + /// Despawns a GameObject. Only call from the server. + /// + /// GameObject to despawn. + /// What happens to the object after being despawned. + public void Despawn(GameObject go, DespawnType? despawnType = null) + { + if (!IsNetworkObjectNull(true)) + _networkObjectCache.Despawn(go, despawnType); + } + /// + /// Despawns a NetworkObject. Only call from the server. + /// + /// NetworkObject to despawn. + /// What happens to the object after being despawned. + public void Despawn(NetworkObject nob, DespawnType? despawnType = null) + { + if (!IsNetworkObjectNull(true)) + _networkObjectCache.Despawn(nob, despawnType); + } + + /// + /// Despawns this _networkObjectCache. Can only be called on the server. + /// + /// What happens to the object after being despawned. + public void Despawn(DespawnType? despawnType = null) + { + if (!IsNetworkObjectNull(true)) + _networkObjectCache.Despawn(despawnType); + } + /// + /// Spawns an object over the network. Can only be called on the server. + /// + /// GameObject instance to spawn. + /// Connection to give ownership to. + public void Spawn(GameObject go, NetworkConnection ownerConnection = null) + { + if (IsNetworkObjectNull(true)) + return; + _networkObjectCache.Spawn(go, ownerConnection); + } + /// + /// Spawns an object over the network. Can only be called on the server. + /// + /// GameObject instance to spawn. + /// Connection to give ownership to. + public void Spawn(NetworkObject nob, NetworkConnection ownerConnection = null) + { + if (IsNetworkObjectNull(true)) + return; + _networkObjectCache.Spawn(nob, ownerConnection); + } + /// + /// Returns if NetworkObject is null. + /// + /// True to throw a warning if null. + /// + private bool IsNetworkObjectNull(bool warn) + { + bool isNull = (_networkObjectCache == null); + if (isNull && warn) + NetworkManager.LogWarning($"NetworkObject is null. This can occur if this object is not spawned, or initialized yet."); + + return isNull; + } + /// + /// Removes ownership from all clients. + /// + public void RemoveOwnership() + { + _networkObjectCache.GiveOwnership(null, true); + } + /// + /// Gives ownership to newOwner. + /// + /// + public void GiveOwnership(NetworkConnection newOwner) + { + _networkObjectCache.GiveOwnership(newOwner, true); + } + + #region Registered components + /// + /// Invokes an action when a specified component becomes registered. Action will invoke immediately if already registered. + /// + /// Component type. + /// Action to invoke. + public void RegisterInvokeOnInstance(Action handler) where T : UnityEngine.Component => _networkObjectCache.RegisterInvokeOnInstance(handler); + /// + /// Removes an action to be invoked when a specified component becomes registered. + /// + /// Component type. + /// Action to invoke. + public void UnregisterInvokeOnInstance(Action handler) where T : UnityEngine.Component => _networkObjectCache.UnregisterInvokeOnInstance(handler); + /// + /// Returns class of type if found within CodegenBase classes. + /// + /// + /// + public T GetInstance() where T : UnityEngine.Component => _networkObjectCache.GetInstance(); + /// + /// Registers a new component to this NetworkManager. + /// + /// Type to register. + /// Reference of the component being registered. + /// True to replace existing references. + public void RegisterInstance(T component, bool replace = true) where T : UnityEngine.Component => _networkObjectCache.RegisterInstance(component, replace); + /// + /// Unregisters a component from this NetworkManager. + /// + /// Type to unregister. + public void UnregisterInstance() where T : UnityEngine.Component => _networkObjectCache.UnregisterInstance(); + #endregion + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.QOL.cs.meta b/Assets/FishNet/Runtime/Object/NetworkBehaviour.QOL.cs.meta new file mode 100644 index 0000000..79aaf06 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.QOL.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d7aef532208d06c4880973c51b1906f5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.RPCLinks.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour.RPCLinks.cs new file mode 100644 index 0000000..11990b3 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.RPCLinks.cs @@ -0,0 +1,140 @@ +using FishNet.Managing.Server; +using FishNet.Object.Helping; +using FishNet.Serializing; +using FishNet.Transporting; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Object +{ + + public abstract partial class NetworkBehaviour : MonoBehaviour + { + #region Private. + /// + /// Link indexes for RPCs. + /// + private Dictionary _rpcLinks = new Dictionary(); + #endregion + + /// + /// Initializes RpcLinks. This will only call once even as host. + /// + private void InitializeOnceRpcLinks() + { + if (NetworkManager.IsServer) + { + /* Link only data from server to clients. While it is + * just as easy to link client to server it's usually + * not needed because server out data is more valuable + * than server in data. */ + /* Links will be stored in the NetworkBehaviour so that + * when the object is destroyed they can be added back + * into availableRpcLinks, within the ServerManager. */ + + ServerManager serverManager = NetworkManager.ServerManager; + //ObserverRpcs. + foreach (uint rpcHash in _observersRpcDelegates.Keys) + { + if (!MakeLink(rpcHash, RpcType.Observers)) + return; + } + //TargetRpcs. + foreach (uint rpcHash in _targetRpcDelegates.Keys) + { + if (!MakeLink(rpcHash, RpcType.Target)) + return; + } + //ReconcileRpcs. + foreach (uint rpcHash in _reconcileRpcDelegates.Keys) + { + if (!MakeLink(rpcHash, RpcType.Reconcile)) + return; + } + + /* Tries to make a link and returns if + * successful. When a link cannot be made the method + * should exit as no other links will be possible. */ + bool MakeLink(uint rpcHash, RpcType rpcType) + { + if (serverManager.GetRpcLink(out ushort linkIndex)) + { + _rpcLinks[rpcHash] = new RpcLinkType(linkIndex, rpcType); + return true; + } + else + { + return false; + } + } + } + } + + /// + /// Returns an estimated length for any Rpc header. + /// + /// + private int GetEstimatedRpcHeaderLength() + { + /* Imaginary number for how long RPC headers are. + * They are well under this value but this exist to + * ensure a writer of appropriate length is pulled + * from the pool. */ + return 20; + } + + /// + /// Creates a PooledWriter and writes the header for a rpc. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private PooledWriter CreateLinkedRpc(RpcLinkType link, PooledWriter methodWriter, Channel channel) + { + int rpcHeaderBufferLength = GetEstimatedRpcHeaderLength(); + int methodWriterLength = methodWriter.Length; + //Writer containing full packet. + PooledWriter writer = WriterPool.GetWriter(rpcHeaderBufferLength + methodWriterLength); + writer.WriteUInt16(link.LinkIndex); + //Write length only if reliable. + if (channel == Channel.Reliable) + writer.WriteLength(methodWriter.Length); + //Data. + writer.WriteArraySegment(methodWriter.GetArraySegment()); + + return writer; + } + + /// + /// Returns RpcLinks the ServerManager. + /// + private void ReturnRpcLinks() + { + if (_rpcLinks.Count == 0) + return; + + ServerManager?.StoreRpcLinks(_rpcLinks); + _rpcLinks.Clear(); + } + + /// + /// Writes rpcLinks to writer. + /// + internal void WriteRpcLinks(Writer writer) + { + PooledWriter rpcLinkWriter = WriterPool.GetWriter(); + foreach (KeyValuePair item in _rpcLinks) + { + //RpcLink index. + rpcLinkWriter.WriteUInt16(item.Value.LinkIndex); + //Hash. + rpcLinkWriter.WriteUInt16((ushort)item.Key); + //True/false if observersRpc. + rpcLinkWriter.WriteByte((byte)item.Value.RpcType); + } + + writer.WriteBytesAndSize(rpcLinkWriter.GetBuffer(), 0, rpcLinkWriter.Length); + rpcLinkWriter.Dispose(); + } + } +} + diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.RPCLinks.cs.meta b/Assets/FishNet/Runtime/Object/NetworkBehaviour.RPCLinks.cs.meta new file mode 100644 index 0000000..3f93ed0 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.RPCLinks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7136e9ee3f7eee44abab09285ab7b939 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.RPCs.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour.RPCs.cs new file mode 100644 index 0000000..1e770f9 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.RPCs.cs @@ -0,0 +1,364 @@ +using FishNet.Connection; +using FishNet.Documenting; +using FishNet.Managing.Logging; +using FishNet.Managing.Transporting; +using FishNet.Object.Delegating; +using FishNet.Serializing; +using FishNet.Serializing.Helping; +using FishNet.Transporting; +using FishNet.Utility.Extension; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Object +{ + + + public abstract partial class NetworkBehaviour : MonoBehaviour + { + #region Private. + /// + /// Registered ServerRpc methods. + /// + private readonly Dictionary _serverRpcDelegates = new Dictionary(); + /// + /// Registered ObserversRpc methods. + /// + private readonly Dictionary _observersRpcDelegates = new Dictionary(); + /// + /// Registered TargetRpc methods. + /// + private readonly Dictionary _targetRpcDelegates = new Dictionary(); + /// + /// Number of total RPC methods for scripts in the same inheritance tree for this instance. + /// + private uint _rpcMethodCount; + /// + /// Size of every rpcHash for this networkBehaviour. + /// + private byte _rpcHashSize = 1; + /// + /// RPCs buffered for new clients. + /// + private Dictionary _bufferedRpcs = new Dictionary(); + /// + /// Connections to exclude from RPCs, such as ExcludeOwner or ExcludeServer. + /// + private HashSet _networkConnectionCache = new HashSet(); + #endregion + + /// + /// Called when buffered RPCs should be sent. + /// + internal void SendBufferedRpcs(NetworkConnection conn) + { + TransportManager tm = _networkObjectCache.NetworkManager.TransportManager; + foreach ((PooledWriter writer, Channel ch) in _bufferedRpcs.Values) + tm.SendToClient((byte)ch, writer.GetArraySegment(), conn); + } + + /// + /// Registers a RPC method. + /// + /// + /// + [APIExclude] + [CodegenMakePublic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected internal void RegisterServerRpc_Internal(uint hash, ServerRpcDelegate del) + { + bool contains = _serverRpcDelegates.ContainsKey(hash); + _serverRpcDelegates[hash] = del; + if (!contains) + IncreaseRpcMethodCount(); + } + /// + /// Registers a RPC method. + /// + /// + /// + [APIExclude] + [CodegenMakePublic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected internal void RegisterObserversRpc_Internal(uint hash, ClientRpcDelegate del) + { + bool contains = _observersRpcDelegates.ContainsKey(hash); + _observersRpcDelegates[hash] = del; + if (!contains) + IncreaseRpcMethodCount(); + } + /// + /// Registers a RPC method. + /// + /// + /// + [APIExclude] + [CodegenMakePublic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected internal void RegisterTargetRpc_Internal(uint hash, ClientRpcDelegate del) + { + bool contains = _targetRpcDelegates.ContainsKey(hash); + _targetRpcDelegates[hash] = del; + if (!contains) + IncreaseRpcMethodCount(); + } + + /// + /// Increases rpcMethodCount and rpcHashSize. + /// + private void IncreaseRpcMethodCount() + { + _rpcMethodCount++; + if (_rpcMethodCount <= byte.MaxValue) + _rpcHashSize = 1; + else + _rpcHashSize = 2; + } + + /// + /// Clears all buffered RPCs for this NetworkBehaviour. + /// + public void ClearBuffedRpcs() + { + foreach ((PooledWriter writer, Channel _) in _bufferedRpcs.Values) + writer.Dispose(); + _bufferedRpcs.Clear(); + } + + /// + /// Reads a RPC hash. + /// + /// + /// + private uint ReadRpcHash(PooledReader reader) + { + if (_rpcHashSize == 1) + return reader.ReadByte(); + else + return reader.ReadUInt16(); + } + /// + /// Called when a ServerRpc is received. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void OnServerRpc(PooledReader reader, NetworkConnection sendingClient, Channel channel) + { + uint methodHash = ReadRpcHash(reader); + + if (sendingClient == null) + { + _networkObjectCache.NetworkManager.LogError($"NetworkConnection is null. ServerRpc {methodHash} on object {gameObject.name} [id {ObjectId}] will not complete. Remainder of packet may become corrupt."); + return; + } + + if (_serverRpcDelegates.TryGetValueIL2CPP(methodHash, out ServerRpcDelegate data)) + data.Invoke(reader, channel, sendingClient); + else + _networkObjectCache.NetworkManager.LogWarning($"ServerRpc not found for hash {methodHash} on object {gameObject.name} [id {ObjectId}]. Remainder of packet may become corrupt."); + } + + /// + /// Called when an ObserversRpc is received. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void OnObserversRpc(uint? methodHash, PooledReader reader, Channel channel) + { + if (methodHash == null) + methodHash = ReadRpcHash(reader); + + if (_observersRpcDelegates.TryGetValueIL2CPP(methodHash.Value, out ClientRpcDelegate del)) + del.Invoke(reader, channel); + else + _networkObjectCache.NetworkManager.LogWarning($"ObserversRpc not found for hash {methodHash.Value} on object {gameObject.name} [id {ObjectId}] . Remainder of packet may become corrupt."); + } + + /// + /// Called when an TargetRpc is received. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void OnTargetRpc(uint? methodHash, PooledReader reader, Channel channel) + { + if (methodHash == null) + methodHash = ReadRpcHash(reader); + + if (_targetRpcDelegates.TryGetValueIL2CPP(methodHash.Value, out ClientRpcDelegate del)) + del.Invoke(reader, channel); + else + _networkObjectCache.NetworkManager.LogWarning($"TargetRpc not found for hash {methodHash.Value} on object {gameObject.name} [id {ObjectId}] . Remainder of packet may become corrupt."); + } + + /// + /// Sends a RPC to server. + /// + /// + /// + /// + [CodegenMakePublic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SendServerRpc_Internal(uint hash, PooledWriter methodWriter, Channel channel) + { + if (!IsSpawnedWithWarning()) + return; + + PooledWriter writer = CreateRpc(hash, methodWriter, PacketId.ServerRpc, channel); + _networkObjectCache.NetworkManager.TransportManager.SendToServer((byte)channel, writer.GetArraySegment()); + writer.DisposeLength(); + } + + /// + /// Sends a RPC to observers. + /// + /// + /// + /// + [APIExclude] + [CodegenMakePublic] //Make internal. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SendObserversRpc_Internal(uint hash, PooledWriter methodWriter, Channel channel, bool buffered, bool excludeServer, bool excludeOwner) + { + if (!IsSpawnedWithWarning()) + return; + + PooledWriter writer; +#if UNITY_EDITOR || DEVELOPMENT_BUILD + if (NetworkManager.DebugManager.ObserverRpcLinks && _rpcLinks.TryGetValueIL2CPP(hash, out RpcLinkType link)) +#else + if (_rpcLinks.TryGetValueIL2CPP(hash, out RpcLinkType link)) +#endif + writer = CreateLinkedRpc(link, methodWriter, channel); + else + writer = CreateRpc(hash, methodWriter, PacketId.ObserversRpc, channel); + + SetNetworkConnectionCache(excludeServer, excludeOwner); + _networkObjectCache.NetworkManager.TransportManager.SendToClients((byte)channel, writer.GetArraySegment(), _networkObjectCache.Observers, _networkConnectionCache, true); + + /* If buffered then dispose of any already buffered + * writers and replace with new one. Writers should + * automatically dispose when references are lost + * anyway but better safe than sorry. */ + if (buffered) + { + if (_bufferedRpcs.TryGetValueIL2CPP(hash, out (PooledWriter pw, Channel ch) result)) + result.pw.DisposeLength(); + _bufferedRpcs[hash] = (writer, channel); + } + //If not buffered then dispose immediately. + else + { + writer.DisposeLength(); + } + } + + /// + /// Sends a RPC to target. + /// + [CodegenMakePublic] //Make internal. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SendTargetRpc_Internal(uint hash, PooledWriter methodWriter, Channel channel, NetworkConnection target, bool excludeServer, bool validateTarget = true) + { + if (!IsSpawnedWithWarning()) + return; + + if (validateTarget) + { + if (target == null) + { + _networkObjectCache.NetworkManager.LogWarning($"Action cannot be completed as no Target is specified."); + return; + } + else + { + //If target is not an observer. + if (!_networkObjectCache.Observers.Contains(target)) + { + _networkObjectCache.NetworkManager.LogWarning($"Action cannot be completed as Target is not an observer for object {gameObject.name} [id {ObjectId}]."); + return; + } + } + } + + //Excluding server. + if (excludeServer && target.IsLocalClient) + return; + + PooledWriter writer; + +#if UNITY_EDITOR || DEVELOPMENT_BUILD + if (NetworkManager.DebugManager.TargetRpcLinks && _rpcLinks.TryGetValueIL2CPP(hash, out RpcLinkType link)) +#else + if (_rpcLinks.TryGetValueIL2CPP(hash, out RpcLinkType link)) +#endif + writer = CreateLinkedRpc(link, methodWriter, channel); + else + writer = CreateRpc(hash, methodWriter, PacketId.TargetRpc, channel); + + _networkObjectCache.NetworkManager.TransportManager.SendToClient((byte)channel, writer.GetArraySegment(), target); + writer.DisposeLength(); + } + + /// + /// Adds excluded connections to ExcludedRpcConnections. + /// + private void SetNetworkConnectionCache(bool addClientHost, bool addOwner) + { + _networkConnectionCache.Clear(); + if (addClientHost && IsClient) + _networkConnectionCache.Add(LocalConnection); + if (addOwner && Owner.IsValid) + _networkConnectionCache.Add(Owner); + } + + + /// + /// Returns if spawned and throws a warning if not. + /// + /// + private bool IsSpawnedWithWarning() + { + bool result = this.IsSpawned; + if (!result) + _networkObjectCache.NetworkManager.LogWarning($"Action cannot be completed as object {gameObject.name} [Id {ObjectId}] is not spawned."); + + return result; + } + + /// + /// Writes a full RPC and returns the writer. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private PooledWriter CreateRpc(uint hash, PooledWriter methodWriter, PacketId packetId, Channel channel) + { + int rpcHeaderBufferLength = GetEstimatedRpcHeaderLength(); + int methodWriterLength = methodWriter.Length; + //Writer containing full packet. + PooledWriter writer = WriterPool.GetWriter(rpcHeaderBufferLength + methodWriterLength); + writer.WritePacketId(packetId); + writer.WriteNetworkBehaviour(this); + //Only write length if reliable. + if (channel == Channel.Reliable) + writer.WriteLength(methodWriterLength + _rpcHashSize); + //Hash and data. + WriteRpcHash(hash, writer); + writer.WriteArraySegment(methodWriter.GetArraySegment()); + + return writer; + } + + /// + /// Writes rpcHash to writer. + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteRpcHash(uint hash, PooledWriter writer) + { + if (_rpcHashSize == 1) + writer.WriteByte((byte)hash); + else + writer.WriteUInt16((byte)hash); + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.RPCs.cs.meta b/Assets/FishNet/Runtime/Object/NetworkBehaviour.RPCs.cs.meta new file mode 100644 index 0000000..b468914 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.RPCs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 938eacb83fa7d0046bd769b31dac7e80 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.SyncTypes.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour.SyncTypes.cs new file mode 100644 index 0000000..4a92bc0 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.SyncTypes.cs @@ -0,0 +1,429 @@ +using FishNet.Connection; +using FishNet.Documenting; +using FishNet.Managing.Logging; +using FishNet.Managing.Transporting; +using FishNet.Object.Synchronizing; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using FishNet.Transporting; +using FishNet.Utility.Extension; +using System.Collections.Generic; +using UnityEngine; + +namespace FishNet.Object +{ + + public abstract partial class NetworkBehaviour : MonoBehaviour + { + #region Types. + /// + /// Used to generate data sent from synctypes. + /// + private class SyncTypeWriter + { + /// + /// Clients which can be synchronized. + /// + public ReadPermission ReadPermission; + /// + /// Writers for each channel. + /// + public PooledWriter[] Writers { get; private set; } + + public SyncTypeWriter(ReadPermission readPermission) + { + ReadPermission = readPermission; + Writers = new PooledWriter[TransportManager.CHANNEL_COUNT]; + for (int i = 0; i < Writers.Length; i++) + Writers[i] = WriterPool.GetWriter(); + } + + /// + /// Resets Writers. + /// + public void Reset() + { + if (Writers == null) + return; + + for (int i = 0; i < Writers.Length; i++) + Writers[i].Reset(); + } + } + #endregion + + #region Private. + /// + /// Writers for syncTypes. A writer will exist for every ReadPermission type. + /// + private SyncTypeWriter[] _syncTypeWriters; + /// + /// SyncVars within this NetworkBehaviour. + /// + private Dictionary _syncVars = new Dictionary(); + /// + /// True if at least one syncVar is dirty. + /// + private bool _syncVarDirty; + /// + /// SyncVars within this NetworkBehaviour. + /// + private Dictionary _syncObjects = new Dictionary(); + /// + /// True if at least one syncObject is dirty. + /// + private bool _syncObjectDirty; + /// + /// All ReadPermission values. + /// + private static ReadPermission[] _readPermissions; + #endregion + + /// + /// Registers a SyncType. + /// + /// + /// + internal void RegisterSyncType(SyncBase sb, uint index) + { + if (sb.IsSyncObject) + _syncObjects.Add(index, sb); + else + _syncVars.Add(index, sb); + } + /// + /// Sets a SyncVar as dirty. + /// + /// True if dirtying a syncObject. + /// True if able to dirty SyncType. + internal bool DirtySyncType(bool isSyncObject) + { + if (!IsServer) + return false; + /* No reason to dirty if there are no observers. + * This can happen even if a client is going to see + * this object because the server side initializes + * before observers are built. */ + if (_networkObjectCache.Observers.Count == 0) + return false; + + bool alreadyDirtied = (isSyncObject) ? _syncObjectDirty : _syncVarDirty; + if (isSyncObject) + _syncObjectDirty = true; + else + _syncVarDirty = true; + + if (!alreadyDirtied) + _networkObjectCache.NetworkManager.ServerManager.Objects.SetDirtySyncType(this, isSyncObject); + + return true; + } + + /// + /// Initializes SyncTypes. This will only call once even as host. + /// + private void InitializeOnceSyncTypes() + { + if (_readPermissions == null) + { + System.Array arr = System.Enum.GetValues(typeof(ReadPermission)); + _readPermissions = new ReadPermission[arr.Length]; + + int count = 0; + foreach (ReadPermission rp in arr) + { + _readPermissions[count] = rp; + count++; + } + } + + //Build writers for observers and owner. + _syncTypeWriters = new SyncTypeWriter[_readPermissions.Length]; + for (int i = 0; i < _syncTypeWriters.Length; i++) + _syncTypeWriters[i] = new SyncTypeWriter(_readPermissions[i]); + + foreach (SyncBase sb in _syncVars.Values) + sb.PreInitialize(_networkObjectCache.NetworkManager); + foreach (SyncBase sb in _syncObjects.Values) + sb.PreInitialize(_networkObjectCache.NetworkManager); + } + + + /// + /// Reads a SyncVar. + /// + /// + internal void OnSyncType(PooledReader reader, int length, bool isSyncObject, bool asServer = false) + { + int readerStart = reader.Position; + while (reader.Position - readerStart < length) + { + byte index = reader.ReadByte(); + if (isSyncObject) + { + if (_syncObjects.TryGetValueIL2CPP(index, out SyncBase sb)) + sb.Read(reader, asServer); + else + NetworkManager.LogWarning($"SyncObject not found for index {index} on {transform.name}. Remainder of packet may become corrupt."); + } + else + { + if (_syncVars.ContainsKey(index)) + ReadSyncVar(reader, index, asServer); + else + NetworkManager.LogWarning($"SyncVar not found for index {index} on {transform.name}. Remainder of packet may become corrupt."); + } + } + } + + /// + /// Codegen overrides this method to read syncVars for each script which inherits NetworkBehaviour. + /// + /// + /// + /// True if reading into SyncVars for the server, false for client. This would be true for predicted spawning if the predicted spawner sent syncvars. + [APIExclude] + internal virtual bool ReadSyncVar(PooledReader reader, uint index, bool asServer) { return false; } + + /// + /// Writers dirty SyncTypes if their write tick has been met. + /// + /// True if there are no pending dirty sync types. + internal bool WriteDirtySyncTypes(bool isSyncObject, bool ignoreInterval = false) + { + /* Can occur when a synctype is queued after + * the object is marked for destruction. This should not + * happen under most conditions since synctypes will be + * pushed through when despawn is called. */ + if (!IsSpawned) + { + ResetSyncTypes(); + return true; + } + + /* If there is nothing dirty then return true, indicating no more + * pending dirty checks. */ + if (isSyncObject && (!_syncObjectDirty || _syncObjects.Count == 0)) + return true; + else if (!isSyncObject && (!_syncVarDirty || _syncVars.Count == 0)) + return true; + + /* True if writers have been reset for this check. + * For perf writers are only reset when data is to be written. */ + bool writersReset = false; + uint tick = _networkObjectCache.NetworkManager.TimeManager.Tick; + + //True if a syncvar is found to still be dirty. + bool dirtyFound = false; + //True if data has been written and is ready to send. + bool dataWritten = false; + Dictionary collection = (isSyncObject) ? _syncObjects : _syncVars; + + foreach (SyncBase sb in collection.Values) + { + if (!sb.IsDirty) + continue; + + dirtyFound = true; + if (ignoreInterval || sb.WriteTimeMet(tick)) + { + //If writers still need to be reset. + if (!writersReset) + { + writersReset = true; + //Reset writers. + for (int i = 0; i < _syncTypeWriters.Length; i++) + _syncTypeWriters[i].Reset(); + } + + //Find channel. + byte channel = (byte)sb.Channel; + sb.ResetDirty(); + //If ReadPermission is owner but no owner skip this syncvar write. + if (sb.Settings.ReadPermission == ReadPermission.OwnerOnly && !_networkObjectCache.Owner.IsValid) + continue; + + dataWritten = true; + //Find PooledWriter to use. + PooledWriter writer = null; + for (int i = 0; i < _syncTypeWriters.Length; i++) + { + if (_syncTypeWriters[i].ReadPermission == sb.Settings.ReadPermission) + { + /* Channel for syncVar is beyond available channels in transport. + * Use default reliable. */ + if (channel >= _syncTypeWriters[i].Writers.Length) + channel = (byte)Channel.Reliable; + + writer = _syncTypeWriters[i].Writers[channel]; + break; + } + } + + if (writer == null) + NetworkManager.LogError($"Writer couldn't be found for permissions {sb.Settings.ReadPermission} on channel {channel}."); + else + sb.WriteDelta(writer); + } + } + + //If no dirty were found. + if (!dirtyFound) + { + if (isSyncObject) + _syncObjectDirty = false; + else + _syncVarDirty = false; + return true; + } + //At least one sync type was dirty. + else if (dataWritten) + { + for (int i = 0; i < _syncTypeWriters.Length; i++) + { + for (byte channel = 0; channel < _syncTypeWriters[i].Writers.Length; channel++) + { + PooledWriter channelWriter = _syncTypeWriters[i].Writers[channel]; + //If there is data to send. + if (channelWriter.Length > 0) + { + using (PooledWriter headerWriter = WriterPool.GetWriter()) + { + //Write the packetId and NB information. + PacketId packetId = (isSyncObject) ? PacketId.SyncObject : PacketId.SyncVar; + headerWriter.WritePacketId(packetId); + PooledWriter dataWriter = WriterPool.GetWriter(); + dataWriter.WriteNetworkBehaviour(this); + + /* SyncVars need length written regardless because amount + * of data being sent per syncvar is unknown, and the packet may have + * additional data after the syncvars. Because of this we should only + * read up to syncvar length then assume the remainder is another packet. + * + * Reliable always has data written as well even if syncObject. This is so + * if an object does not exist for whatever reason the packet can be + * recovered by skipping the data. + * + * Realistically everything will be a syncvar or on the reliable channel unless + * the user makes a custom syncobject that utilizes unreliable. */ + if (!isSyncObject || (Channel)channel == Channel.Reliable) + dataWriter.WriteBytesAndSize(channelWriter.GetBuffer(), 0, channelWriter.Length); + else + dataWriter.WriteBytes(channelWriter.GetBuffer(), 0, channelWriter.Length); + + //Attach data onto packetWriter. + headerWriter.WriteArraySegment(dataWriter.GetArraySegment()); + dataWriter.Dispose(); + + + //If only sending to owner. + if (_syncTypeWriters[i].ReadPermission == ReadPermission.OwnerOnly) + { + _networkObjectCache.NetworkManager.TransportManager.SendToClient(channel, headerWriter.GetArraySegment(), _networkObjectCache.Owner); + } + //Sending to observers. + else + { + bool excludeOwner = (_syncTypeWriters[i].ReadPermission == ReadPermission.ExcludeOwner); + SetNetworkConnectionCache(false, excludeOwner); + NetworkConnection excludedConnection = (excludeOwner) ? _networkObjectCache.Owner : null; + _networkObjectCache.NetworkManager.TransportManager.SendToClients((byte)channel, headerWriter.GetArraySegment(), _networkObjectCache.Observers, _networkConnectionCache); + + } + + + + } + } + } + } + } + + /* Fall through. If here then sync types are still pending + * being written or were just written this frame. */ + return false; + } + + + /// + /// Resets all SyncTypes for this NetworkBehaviour for server and client side. + /// + internal void ResetSyncTypes() + { + foreach (SyncBase item in _syncVars.Values) + item.Reset(); + foreach (SyncBase item in _syncObjects.Values) + item.Reset(); + + _syncObjectDirty = false; + _syncVarDirty = false; + } + + + /// + /// Resets all SyncTypes for this NetworkBehaviour. + /// + internal void ResetSyncTypes(bool asServer) + { + if (asServer || (!asServer && !IsServer)) + { + foreach (SyncBase item in _syncVars.Values) + item.Reset(); + foreach (SyncBase item in _syncObjects.Values) + item.Reset(); + } + } + + /// + /// Writers syncVars for a spawn message. + /// + internal void WriteSyncTypesForSpawn(PooledWriter writer, SyncTypeWriteType writeType) + { + //Write for owner if writing all or owner, but not observers. + bool ownerWrite = (writeType != SyncTypeWriteType.Observers); + WriteSyncType(_syncVars); + WriteSyncType(_syncObjects); + + void WriteSyncType(Dictionary collection) + { + using (PooledWriter syncTypeWriter = WriterPool.GetWriter()) + { + /* Since all values are being written everything is + * written in order so there's no reason to pass + * indexes. */ + foreach (SyncBase sb in collection.Values) + { + //If not for owner and syncvar is owner only. + if (!ownerWrite && sb.Settings.ReadPermission == ReadPermission.OwnerOnly) + { + //If there is an owner then skip. + if (_networkObjectCache.Owner.IsValid) + continue; + } + + sb.WriteFull(syncTypeWriter); + } + + writer.WriteBytesAndSize(syncTypeWriter.GetBuffer(), 0, syncTypeWriter.Length); + } + } + } + + + /// + /// Manually marks a SyncType as dirty, be it SyncVar or SyncObject. + /// + /// SyncType variable to dirty. + protected void DirtySyncType(object syncType) + { + /* This doesn't actually do anything. + * The codegen replaces calls to this method + * with a Dirty call for syncType. */ + } + + + } + + +} + diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.SyncTypes.cs.meta b/Assets/FishNet/Runtime/Object/NetworkBehaviour.SyncTypes.cs.meta new file mode 100644 index 0000000..2d0f47e --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.SyncTypes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e56d5389eb07aa040b8a9ec8b0d7c597 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour.cs new file mode 100644 index 0000000..750bb12 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.cs @@ -0,0 +1,190 @@ +using FishNet.Documenting; +using FishNet.Serializing.Helping; +using FishNet.Utility.Constant; +using System.Runtime.CompilerServices; +using UnityEngine; + +[assembly: InternalsVisibleTo(UtilityConstants.CODEGEN_ASSEMBLY_NAME)] +namespace FishNet.Object +{ + /// + /// Scripts which inherit from NetworkBehaviour can be used to gain insight of, and perform actions on the network. + /// + public abstract partial class NetworkBehaviour : MonoBehaviour + { + /// + /// True if this NetworkBehaviour is initialized for the network. + /// + public bool IsSpawned => _networkObjectCache.IsSpawned; + /// + /// + /// + [SerializeField, HideInInspector] + private byte _componentIndexCache = byte.MaxValue; + /// + /// ComponentIndex for this NetworkBehaviour. + /// + public byte ComponentIndex + { + get => _componentIndexCache; + private set => _componentIndexCache = value; + } +#if UNITY_EDITOR + /// + /// NetworkObject automatically added or discovered during edit time. + /// + [SerializeField, HideInInspector] + private NetworkObject _addedNetworkObject; +#endif + /// + /// + /// + [SerializeField, HideInInspector] + private NetworkObject _networkObjectCache; + /// + /// NetworkObject this behaviour is for. + /// + public NetworkObject NetworkObject => _networkObjectCache; + + /// + /// Initializes this script. This will only run once even as host. + /// + /// + /// + internal void InitializeOnce_Internal() + { + InitializeOnceSyncTypes(); + InitializeOnceRpcLinks(); + } + + + /// + /// Serializes information for network components. + /// + internal void SerializeComponents(NetworkObject nob, byte componentIndex) + { + _networkObjectCache = nob; + ComponentIndex = componentIndex; + } + + /// + /// Manually initializes network content for the NetworkBehaviour if the object it's on is disabled. + /// + internal void InitializeIfDisabled() + { + if (gameObject.activeInHierarchy) + return; + + NetworkInitializeIfDisabled(); + } + /// + /// Long name is to prevent users from potentially creating their own method named the same. + /// + [CodegenMakePublic] + [APIExclude] + internal virtual void NetworkInitializeIfDisabled() { } + + #region Editor. + protected virtual void Reset() + { +#if UNITY_EDITOR + if (Application.isPlaying) + return; + + TryAddNetworkObject(); +#endif + } + + protected virtual void OnValidate() + { +#if UNITY_EDITOR + if (Application.isPlaying) + return; + + TryAddNetworkObject(); +#endif + } + + /// + /// Resets this NetworkBehaviour so that it may be added to an object pool. + /// + internal void ResetForObjectPool() + { + ResetSyncTypes(); + ClearReplicateCache(); + ClearBuffedRpcs(); + } + + + /// + /// Tries to add the NetworkObject component. + /// + private NetworkObject TryAddNetworkObject() + { +#if UNITY_EDITOR + if (Application.isPlaying) + return _addedNetworkObject; + + if (_addedNetworkObject != null) + { + AlertToDuplicateNetworkObjects(_addedNetworkObject.transform); + return _addedNetworkObject; + } + + /* Manually iterate up the chain because GetComponentInParent doesn't + * work when modifying prefabs in the inspector. Unity, you're starting + * to suck a lot right now. */ + NetworkObject result = null; + Transform climb = transform; + + while (climb != null) + { + if (climb.TryGetComponent(out result)) + break; + else + climb = climb.parent; + } + + if (result != null) + { + _addedNetworkObject = result; + } + //Not found, add a new nob. + else + { + _addedNetworkObject = transform.root.gameObject.AddComponent(); + Debug.Log($"Script {GetType().Name} on object {gameObject.name} added a NetworkObject component to {transform.root.name}."); + } + + AlertToDuplicateNetworkObjects(_addedNetworkObject.transform); + return _addedNetworkObject; + + //Removes duplicate network objects from t. + void AlertToDuplicateNetworkObjects(Transform t) + { + NetworkObject[] nobs = t.GetComponents(); + //This shouldn't be possible but does occur sometimes; maybe a unity bug? + if (nobs.Length > 1) + { + //Update added to first entryt. + _addedNetworkObject = nobs[0]; + + string useMenu = " You may also use the Fish-Networking menu to automatically remove duplicate NetworkObjects."; + string sceneName = t.gameObject.scene.name; + if (string.IsNullOrEmpty(sceneName)) + Debug.LogError($"Prefab {t.name} has multiple NetworkObject components. Please remove the extra component(s) to prevent errors.{useMenu}"); + else + Debug.LogError($"Object {t.name} in scene {sceneName} has multiple NetworkObject components. Please remove the extra component(s) to prevent errors.{useMenu}"); + } + + } +#else + return null; +#endif + } + + #endregion + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.cs.meta b/Assets/FishNet/Runtime/Object/NetworkBehaviour.cs.meta new file mode 100644 index 0000000..6188375 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d2230f9cdb1ffc9489b53875c963342d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/NetworkObject.Broadcast.cs b/Assets/FishNet/Runtime/Object/NetworkObject.Broadcast.cs new file mode 100644 index 0000000..14459b7 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject.Broadcast.cs @@ -0,0 +1,31 @@ +using FishNet.Broadcast; +using FishNet.Managing; +using FishNet.Transporting; +using UnityEngine; + +namespace FishNet.Object +{ + public sealed partial class NetworkObject : MonoBehaviour + { + + /// + /// Sends a broadcast to Observers on this NetworkObject. + /// + /// Type of broadcast to send. + /// Broadcast data being sent; for example: an instance of your broadcast type. + /// True if the client must be authenticated for this broadcast to send. + /// Channel to send on. + public void Broadcast(T message, bool requireAuthenticated = true, Channel channel = Channel.Reliable) where T : struct, IBroadcast + { + if (NetworkManager == null) + { + NetworkManager.StaticLogWarning($"Cannot send broadcast from {gameObject.name}, NetworkManager reference is null. This may occur if the object is not spawned or initialized."); + return; + } + + NetworkManager.ServerManager.Broadcast(Observers, message, requireAuthenticated, channel); + } + } + +} + diff --git a/Assets/FishNet/Runtime/Object/NetworkObject.Broadcast.cs.meta b/Assets/FishNet/Runtime/Object/NetworkObject.Broadcast.cs.meta new file mode 100644 index 0000000..4068fcb --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject.Broadcast.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 55d793117b52da549affcc9ec30b05c0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/NetworkObject.Callbacks.cs b/Assets/FishNet/Runtime/Object/NetworkObject.Callbacks.cs new file mode 100644 index 0000000..edc700c --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject.Callbacks.cs @@ -0,0 +1,150 @@ +using FishNet.Connection; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Object +{ + public sealed partial class NetworkObject : MonoBehaviour + { + /// + /// Called after all data is synchronized with this NetworkObject. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void InitializeCallbacks(bool asServer, bool invokeSyncTypeCallbacks) + { + /* Note: When invoking OnOwnership here previous owner will + * always be an empty connection, since the object is just + * now initializing. */ + + if (!asServer) + ClientInitialized = true; + + //Set that client or server is active before callbacks. + SetActiveStatus(true, asServer); + + //Invoke OnStartNetwork. + for (int i = 0; i < NetworkBehaviours.Length; i++) + NetworkBehaviours[i].InvokeOnNetwork(true); + + //As server. + if (asServer) + { + for (int i = 0; i < NetworkBehaviours.Length; i++) + NetworkBehaviours[i].OnStartServer(); + for (int i = 0; i < NetworkBehaviours.Length; i++) + NetworkBehaviours[i].OnOwnershipServer(FishNet.Managing.NetworkManager.EmptyConnection); + if (invokeSyncTypeCallbacks) + InvokeSyncTypeCallbacks(true); + } + //As client. + else + { + for (int i = 0; i < NetworkBehaviours.Length; i++) + NetworkBehaviours[i].OnStartClient(); + for (int i = 0; i < NetworkBehaviours.Length; i++) + NetworkBehaviours[i].OnOwnershipClient(FishNet.Managing.NetworkManager.EmptyConnection); + if (invokeSyncTypeCallbacks) + InvokeSyncTypeCallbacks(false); + } + } + + + /// + /// Invokes pending SyncType callbacks. + /// + /// + internal void InvokeSyncTypeCallbacks(bool asServer) + { + for (int i = 0; i < NetworkBehaviours.Length; i++) + NetworkBehaviours[i].InvokeSyncTypeCallbacks(asServer); + } + + /// + /// Invokes events to be called after OnServerStart. + /// This is made one method to save instruction calls. + /// + /// + internal void InvokePostOnServerStart(NetworkConnection conn) + { + for (int i = 0; i < NetworkBehaviours.Length; i++) + NetworkBehaviours[i].SendBufferedRpcs(conn); + + for (int i = 0; i < NetworkBehaviours.Length; i++) + NetworkBehaviours[i].OnSpawnServer(conn); + } + + /// + /// Called on the server before it sends a despawn message to a client. + /// + /// Connection spawn was sent to. + internal void InvokeOnServerDespawn(NetworkConnection conn) + { + for (int i = 0; i < NetworkBehaviours.Length; i++) + NetworkBehaviours[i].OnDespawnServer(conn); + } + + /// + /// Invokes OnStop callbacks. + /// + /// + internal void InvokeStopCallbacks(bool asServer) + { + if (asServer) + { + for (int i = 0; i < NetworkBehaviours.Length; i++) + NetworkBehaviours[i].OnStopServer(); + } + else + { + for (int i = 0; i < NetworkBehaviours.Length; i++) + NetworkBehaviours[i].OnStopClient(); + } + + /* Invoke OnStopNetwork if server is calling + * or if client and not as server. */ + if (asServer || (!asServer && !IsServer)) + { + for (int i = 0; i < NetworkBehaviours.Length; i++) + NetworkBehaviours[i].InvokeOnNetwork(false); + } + + if (asServer) + IsServer = false; + else + IsClient = false; + } + + /// + /// Invokes OnOwnership callbacks. + /// + /// + private void InvokeOwnership(NetworkConnection prevOwner, bool asServer) + { + if (asServer) + { + for (int i = 0; i < NetworkBehaviours.Length; i++) + NetworkBehaviours[i].OnOwnershipServer(prevOwner); + } + else + { + /* If local client is owner and not server then only + * invoke if the prevOwner is different. This prevents + * the owner change callback from happening twice when + * using TakeOwnership. + * + * Further explained, the TakeOwnership sets local client + * as owner client-side, which invokes the OnOwnership method. + * Then when the server approves the owner change it would invoke + * again, which is not needed. */ + bool blockInvoke = ((IsOwner && !IsServer) && (prevOwner == Owner)); + if (!blockInvoke) + { + for (int i = 0; i < NetworkBehaviours.Length; i++) + NetworkBehaviours[i].OnOwnershipClient(prevOwner); + } + } + } + } + +} + diff --git a/Assets/FishNet/Runtime/Object/NetworkObject.Callbacks.cs.meta b/Assets/FishNet/Runtime/Object/NetworkObject.Callbacks.cs.meta new file mode 100644 index 0000000..21b4bb7 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject.Callbacks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8e9fbf0d6eb10e94d892dd4e817030bc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/NetworkObject.Observers.cs b/Assets/FishNet/Runtime/Object/NetworkObject.Observers.cs new file mode 100644 index 0000000..e39a968 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject.Observers.cs @@ -0,0 +1,213 @@ +using FishNet.Connection; +using FishNet.Observing; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Object +{ + public sealed partial class NetworkObject : MonoBehaviour + { + #region Public. + /// + /// Called when this NetworkObject losses all observers or gains observers while previously having none. + /// + public event Action OnObserversActive; + /// + /// NetworkObserver on this object. + /// + [HideInInspector] + public NetworkObserver NetworkObserver = null; + /// + /// Clients which can see and get messages from this NetworkObject. + /// + public HashSet Observers = new HashSet(); + #endregion + + #region Private. + /// + /// True if NetworkObserver has been initialized. + /// + private bool _networkObserverInitiliazed = false; + /// + /// Found renderers on the NetworkObject and it's children. This is only used as clientHost to hide non-observers objects. + /// + [System.NonSerialized] + private Renderer[] _renderers; + /// + /// True if renderers have been looked up. + /// + private bool _renderersPopulated; + /// + /// Last visibility value for clientHost on this object. + /// + private bool _lastClientHostVisibility; + #endregion + + /// + /// Updates cached renderers used to managing clientHost visibility. + /// + /// True to also update visibility if clientHost. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void UpdateRenderers(bool updateVisibility = true) + { + UpdateRenderers_Internal(updateVisibility); + } + + /// + /// Sets the renderer visibility for clientHost. + /// + /// True if renderers are to be visibile. + /// True to skip blocking checks. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetRenderersVisible(bool visible, bool force = false) + { + if (!force) + { + if (!NetworkObserver.UpdateHostVisibility) + return; + } + + if (!_renderersPopulated) + { + UpdateRenderers_Internal(false); + _renderersPopulated = true; + } + + UpdateRenderVisibility(visible); + } + + /// + /// Clears and updates renderers. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void UpdateRenderers_Internal(bool updateVisibility) + { + _renderers = GetComponentsInChildren(true); + List enabledRenderers = new List(); + foreach (Renderer r in _renderers) + { + if (r.enabled) + enabledRenderers.Add(r); + } + //If there are any disabled renderers then change _renderers to cached values. + if (enabledRenderers.Count != _renderers.Length) + _renderers = enabledRenderers.ToArray(); + + if (updateVisibility) + UpdateRenderVisibility(_lastClientHostVisibility); + } + + /// + /// Updates visibilites on renders without checks. + /// + /// + private void UpdateRenderVisibility(bool visible) + { + bool rebuildRenderers = false; + + Renderer[] rs = _renderers; + int count = rs.Length; + for (int i = 0; i < count; i++) + { + Renderer r = rs[i]; + if (r == null) + { + rebuildRenderers = true; + break; + } + + r.enabled = visible; + } + + _lastClientHostVisibility = visible; + //If to rebuild then do so, while updating visibility. + if (rebuildRenderers) + UpdateRenderers(true); + } + + /// + /// Adds the default NetworkObserver conditions using the ObserverManager. + /// + private void AddDefaultNetworkObserverConditions() + { + if (_networkObserverInitiliazed) + return; + + NetworkObserver = NetworkManager.ObserverManager.AddDefaultConditions(this); + } + + + /// + /// Removes a connection from observers for this object returning if the connection was removed. + /// + /// + internal bool RemoveObserver(NetworkConnection connection) + { + int startCount = Observers.Count; + bool removed = Observers.Remove(connection); + if (removed) + TryInvokeOnObserversActive(startCount); + + return removed; + } + + /// + /// Adds the connection to observers if conditions are met. + /// + /// + /// True if added to Observers. + internal ObserverStateChange RebuildObservers(NetworkConnection connection, bool timedOnly) + { + //If not a valid connection. + if (!connection.IsValid) + { + NetworkManager.LogWarning($"An invalid connection was used when rebuilding observers."); + return ObserverStateChange.Unchanged; + } + //Valid not not active. + else if (!connection.IsActive) + { + /* Just remove from observers since connection isn't active + * and return unchanged because nothing should process + * given the connection isnt active. */ + Observers.Remove(connection); + return ObserverStateChange.Unchanged; + } + else if (IsDeinitializing) + { + /* If object is deinitializing it's either being despawned + * this frame or it's not spawned. If we've made it this far, + * it's most likely being despawned. */ + return ObserverStateChange.Unchanged; + } + + int startCount = Observers.Count; + ObserverStateChange osc = NetworkObserver.RebuildObservers(connection, timedOnly); + if (osc == ObserverStateChange.Added) + Observers.Add(connection); + else if (osc == ObserverStateChange.Removed) + Observers.Remove(connection); + + if (osc != ObserverStateChange.Unchanged) + TryInvokeOnObserversActive(startCount); + + return osc; + } + + /// + /// Invokes OnObserversActive if observers are now 0 but previously were not, or if was previously 0 but now has observers. + /// + /// + private void TryInvokeOnObserversActive(int startCount) + { + if ((Observers.Count > 0 && startCount == 0) || + Observers.Count == 0 && startCount > 0) + OnObserversActive?.Invoke(this); + } + + } + +} + diff --git a/Assets/FishNet/Runtime/Object/NetworkObject.Observers.cs.meta b/Assets/FishNet/Runtime/Object/NetworkObject.Observers.cs.meta new file mode 100644 index 0000000..2024e29 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject.Observers.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 816dbea70a70ab949a44f485155f0087 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/NetworkObject.QOL.cs b/Assets/FishNet/Runtime/Object/NetworkObject.QOL.cs new file mode 100644 index 0000000..076a756 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject.QOL.cs @@ -0,0 +1,313 @@ +using FishNet.Component.ColliderRollback; +using FishNet.Connection; +using FishNet.Managing; +using FishNet.Managing.Client; +using FishNet.Managing.Observing; +using FishNet.Managing.Predicting; +using FishNet.Managing.Scened; +using FishNet.Managing.Server; +using FishNet.Managing.Timing; +using FishNet.Managing.Transporting; +using System; +using UnityEngine; + +namespace FishNet.Object +{ + public sealed partial class NetworkObject : MonoBehaviour + { + #region Public. + /// + /// True if predicted spawning is allowed for this object. + /// + internal bool AllowPredictedSpawning => (PredictedSpawn == null) ? false : PredictedSpawn.GetAllowSpawning(); + /// + /// True if predicted spawning is allowed for this object. + /// + internal bool AllowPredictedDespawning => (PredictedSpawn == null) ? false : PredictedSpawn.GetAllowDespawning(); + /// + /// True to allow clients to predicted set syncTypes prior to spawning the item. Set values will be applied on the server and sent to other clients. + /// + internal bool AllowPredictedSyncTypes => (PredictedSpawn == null) ? false : PredictedSpawn.GetAllowSyncTypes(); + /// + /// True if this object has been initialized on the client side. + /// This is set true right before client callbacks. + /// + public bool ClientInitialized { get; private set; } + /// + /// + /// + private bool _isClient; + /// + /// True if the client is active and authenticated. + /// + public bool IsClient + { + /* This needs to use a special check when + * player is acting as host. Clients won't + * set IsClient until they receive the spawn message + * but the user may expect this true after client + * gains observation but before client gets spawn. */ + get + { + if (IsServer) + return (NetworkManager == null) ? false : NetworkManager.IsClient; + else + return _isClient; + } + + private set => _isClient = value; + } + + /// + /// True if only the client is active and authenticated. + /// + public bool IsClientOnly => (IsClient && !IsServer); + /// + /// True if server is active. + /// + public bool IsServer { get; private set; } + /// + /// True if only the server is active. + /// + public bool IsServerOnly => (IsServer && !IsClient); + /// + /// True if client and server are active. + /// + public bool IsHost => (IsClient && IsServer); + /// + /// True if client nor server are active. + /// + public bool IsOffline => (!IsClient && !IsServer); + /// + /// True if the local client is the owner of this object. + /// + public bool IsOwner + { + get + { + /* ClientInitialized becomes true when this + * NetworkObject has been initialized on the client side. + * + * This value is used to prevent IsOwner from returning true + * when running as host; primarily in Update or Tick callbacks + * where IsOwner would be true as host but OnStartClient has + * not called yet. + * + * EG: server will set owner when it spawns the object. + * If IsOwner is checked before the object spawns on the + * client-host then it would also return true, since the + * Owner reference would be the same as what was set by server. + * + * This is however bad when the client hasn't initialized the object + * yet because it gives a false sense of execution order. + * As a result, Update or Ticks may return IsOwner as true well before OnStartClient + * is called. Many users rightfully create code with the assumption the client has been + * initialized by the time IsOwner is true. + * + * This is a double edged sword though because now IsOwner would return true + * within OnStartNetwork for clients only, but not for host given the client + * side won't be initialized yet as host. As a work around CodeAnalysis will + * inform users to instead use base.Owner.IsLocalClient within OnStartNetwork. */ + if (!ClientInitialized) + return false; + + return Owner.IsLocalClient; + } + } + + /// + /// + /// + private NetworkConnection _owner; + /// + /// Owner of this object. + /// + public NetworkConnection Owner + { + get + { + //Ensures a null Owner is never returned. + if (_owner == null) + return FishNet.Managing.NetworkManager.EmptyConnection; + + return _owner; + } + private set { _owner = value; } + } + /// + /// ClientId for this NetworkObject owner. + /// + public int OwnerId => (!Owner.IsValid) ? -1 : Owner.ClientId; + /// + /// True if the object is initialized for the network. + /// + public bool IsSpawned => (!IsDeinitializing && ObjectId != NetworkObject.UNSET_OBJECTID_VALUE); + /// + /// The local connection of the client calling this method. + /// + public NetworkConnection LocalConnection => (NetworkManager == null) ? new NetworkConnection() : NetworkManager.ClientManager.Connection; + /// + /// NetworkManager for this object. + /// + public NetworkManager NetworkManager { get; private set; } + /// + /// ServerManager for this object. + /// + public ServerManager ServerManager { get; private set; } + /// + /// ClientManager for this object. + /// + public ClientManager ClientManager { get; private set; } + /// + /// ObserverManager for this object. + /// + public ObserverManager ObserverManager { get; private set; } + /// + /// TransportManager for this object. + /// + public TransportManager TransportManager { get; private set; } + /// + /// TimeManager for this object. + /// + public TimeManager TimeManager { get; private set; } + /// + /// SceneManager for this object. + /// + public SceneManager SceneManager { get; private set; } + /// + /// PredictionManager for this object. + /// + public PredictionManager PredictionManager {get;private set;} + /// + /// RollbackManager for this object. + /// + public RollbackManager RollbackManager { get; private set; } + #endregion + + /// + /// Returns a NetworkBehaviour on this NetworkObject. + /// + /// ComponentIndex of the NetworkBehaviour. + /// True to error if not found. + /// + public NetworkBehaviour GetNetworkBehaviour(byte componentIndex, bool error) + { + if (componentIndex >= NetworkBehaviours.Length) + { + if (error) + { + string message = $"ComponentIndex of {componentIndex} is out of bounds on {gameObject.name} [id {ObjectId}]. This may occur if you have modified your gameObject/prefab without saving it, or the scene."; + if (NetworkManager == null) + NetworkManager.StaticLogError(message); + else + NetworkManager.LogError(message); + + } + } + + return NetworkBehaviours[componentIndex]; + } + + /// + /// Despawns a GameObject. Only call from the server. + /// + /// GameObject to despawn. + /// What happens to the object after being despawned. + public void Despawn(GameObject go, DespawnType? despawnType = null) + { + NetworkManager?.ServerManager.Despawn(go, despawnType); + } + /// + /// Despawns a NetworkObject. Only call from the server. + /// + /// NetworkObject to despawn. + /// What happens to the object after being despawned. + public void Despawn(NetworkObject nob, DespawnType? despawnType = null) + { + NetworkManager?.ServerManager.Despawn(nob, despawnType); + } + /// + /// Despawns this NetworkObject. Only call from the server. + /// + /// What happens to the object after being despawned. + public void Despawn(DespawnType? despawnType = null) + { + NetworkObject nob = this; + NetworkManager?.ServerManager.Despawn(nob, despawnType); + } + /// + /// Spawns an object over the network. Only call from the server. + /// + public void Spawn(GameObject go, NetworkConnection ownerConnection = null) + { + NetworkManager?.ServerManager.Spawn(go, ownerConnection); + } + /// + /// Spawns an object over the network. Only call from the server. + /// + public void Spawn(NetworkObject nob, NetworkConnection ownerConnection = null) + { + NetworkManager?.ServerManager.Spawn(nob, ownerConnection); + } + + /// + /// Takes ownership of this object and child network objects, allowing immediate control. + /// + /// Connection to give ownership to. + public void SetLocalOwnership(NetworkConnection caller) + { + NetworkConnection prevOwner = Owner; + SetOwner(caller); + + int count; + count = NetworkBehaviours.Length; + for (int i = 0; i < count; i++) + NetworkBehaviours[i].OnOwnershipClient(prevOwner); + count = ChildNetworkObjects.Count; + for (int i = 0; i < count; i++) + ChildNetworkObjects[i].SetLocalOwnership(caller); + } + + #region Registered components + /// + /// Invokes an action when a specified component becomes registered. Action will invoke immediately if already registered. + /// + /// Component type. + /// Action to invoke. + public void RegisterInvokeOnInstance(Action handler) where T : UnityEngine.Component => NetworkManager.RegisterInvokeOnInstance(handler); + /// + /// Removes an action to be invoked when a specified component becomes registered. + /// + /// Component type. + /// Action to invoke. + public void UnregisterInvokeOnInstance(Action handler) where T : UnityEngine.Component => NetworkManager.UnregisterInvokeOnInstance(handler); + /// + /// Returns if an instance exists for type. + /// + /// + /// + public bool HasInstance() where T : UnityEngine.Component => NetworkManager.HasInstance(); + /// + /// Returns class of type if found within CodegenBase classes. + /// + /// + /// + public T GetInstance() where T : UnityEngine.Component => NetworkManager.GetInstance(); + /// + /// Registers a new component to this NetworkManager. + /// + /// Type to register. + /// Reference of the component being registered. + /// True to replace existing references. + public void RegisterInstance(T component, bool replace = true) where T : UnityEngine.Component => NetworkManager.RegisterInstance(component, replace); + /// + /// Unregisters a component from this NetworkManager. + /// + /// Type to unregister. + public void UnregisterInstance() where T : UnityEngine.Component => NetworkManager.UnregisterInstance(); + #endregion + + } + +} + diff --git a/Assets/FishNet/Runtime/Object/NetworkObject.QOL.cs.meta b/Assets/FishNet/Runtime/Object/NetworkObject.QOL.cs.meta new file mode 100644 index 0000000..422ef3f --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject.QOL.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fd30b4b61d50d01499c94a63a6eeb863 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/NetworkObject.ReferenceIds.cs b/Assets/FishNet/Runtime/Object/NetworkObject.ReferenceIds.cs new file mode 100644 index 0000000..f5eeb08 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject.ReferenceIds.cs @@ -0,0 +1,213 @@ +using UnityEngine; +using System.Collections.Generic; +using System; +using FishNet.Object.Helping; +using System.Linq; +#if UNITY_EDITOR +using UnityEditor.Experimental.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditor; +#endif + +namespace FishNet.Object +{ + public sealed partial class NetworkObject : MonoBehaviour + { + #region Serialized. + + /// + /// Networked PrefabId assigned to this Prefab. + /// + [field: SerializeField, HideInInspector] + public ushort PrefabId { get; internal set; } = 0; + /// + /// Spawn collection to use assigned to this Prefab. + /// + [field: SerializeField, HideInInspector] + public ushort SpawnableCollectionId { get; internal set; } = 0; +#pragma warning disable 414 //Disabled because Unity thinks tihs is unused when building. + /// + /// Hash to the scene which this object resides. + /// + [SerializeField, HideInInspector] + private uint _scenePathHash; +#pragma warning restore 414 + /// + /// Network Id for this scene object. + /// + [field: SerializeField, HideInInspector] + internal ulong SceneId { get; private set; } + /// + /// Hash for the path which this asset resides. This value is set during edit time. + /// + [field: SerializeField, HideInInspector] + public ulong AssetPathHash { get; private set; } + /// + /// Sets AssetPathhash value. + /// + /// Value to use. + public void SetAssetPathHash(ulong value) => AssetPathHash = value; + #endregion + +#if UNITY_EDITOR + /// + /// This is used to store NetworkObjects in the scene during edit time. + /// SceneIds are compared against this collection to ensure there are no duplicated. + /// + [SerializeField, HideInInspector] + private List _sceneNetworkObjects = new List(); +#endif + + /// + /// Removes SceneObject state. + /// This may only be called at runtime. + /// + internal void ClearRuntimeSceneObject() + { + if (!Application.isPlaying) + { + Debug.LogError($"ClearRuntimeSceneObject may only be called at runtime."); + return; + } + + SceneId = 0; + } + +#if UNITY_EDITOR + /// + /// Tries to generate a SceneId. + /// + internal void TryCreateSceneID() + { + if (Application.isPlaying) + return; + //Unity bug, sometimes this can be null depending on editor callback orders. + if (gameObject == null) + return; + //Not a scene object. + if (string.IsNullOrEmpty(gameObject.scene.name)) + { + SceneId = 0; + return; + } + + ulong startId = SceneId; + uint startPath = _scenePathHash; + + ulong sceneId = 0; + uint scenePathHash = 0; + //If prefab or part of a prefab, not a scene object. + if (PrefabUtility.IsPartOfPrefabAsset(this) || IsEditingInPrefabMode() || + //Not in a scene, another prefab check. + !gameObject.scene.IsValid() || + //Stored on disk, so is a prefab. Somehow prefabutility missed it. + EditorUtility.IsPersistent(this)) + { + //These are all failing conditions, don't do additional checks. + } + else + { + System.Random rnd = new System.Random(); + scenePathHash = gameObject.scene.path.ToLower().GetStableHash32(); + sceneId = SceneId; + //Not a valid sceneId or is a duplicate. + if (scenePathHash != _scenePathHash || SceneId == 0 || IsDuplicateSceneId(SceneId)) + { + /* If a scene has not been opened since an id has been + * generated then it will not be serialized in editor. The id + * would be correct in build but not if running in editor. + * Should conditions be true where scene is building without + * being opened then cancel build and request user to open and save + * scene. */ + if (BuildPipeline.isBuildingPlayer) + throw new InvalidOperationException($"Networked GameObject {gameObject.name} in scene {gameObject.scene.path} is missing a SceneId. Open the scene, select the Fish-Networking menu, and choose Rebuild SceneIds. If the problem persist ensures {gameObject.name} does not have any missing script references on it's prefab or in the scene. Also ensure that you have any prefab changes for the object applied."); + + ulong shiftedHash = (ulong)scenePathHash << 32; + ulong randomId = 0; + while (randomId == 0 || IsDuplicateSceneId(randomId)) + { + uint next = (uint)(rnd.Next(int.MinValue, int.MaxValue) + int.MaxValue); + /* Since the collection is lost when a scene loads the it's possible to + * have a sceneid from another scene. Because of this the scene path is + * inserted into the sceneid. */ + randomId = (next & 0xFFFFFFFF) | shiftedHash; + } + + sceneId = randomId; + } + + } + + bool idChanged = (sceneId != startId); + bool pathChanged = (startPath != scenePathHash); + //If either changed then dirty and set. + if (idChanged || pathChanged) + { + //Set dirty so changes will be saved. + EditorUtility.SetDirty(this); + /* Add to sceneIds collection. This must be done + * even if a new sceneId was not generated because + * the collection information is lost when the + * scene is existed. Essentially, it gets repopulated + * when the scene is re-opened. */ + SceneId = sceneId; + _scenePathHash = scenePathHash; + } + } + + private bool IsEditingInPrefabMode() + { + if (EditorUtility.IsPersistent(this)) + { + // if the game object is stored on disk, it is a prefab of some kind, despite not returning true for IsPartOfPrefabAsset =/ + return true; + } + else + { + // If the GameObject is not persistent let's determine which stage we are in first because getting Prefab info depends on it + StageHandle mainStage = StageUtility.GetMainStageHandle(); + StageHandle currentStage = StageUtility.GetStageHandle(gameObject); + if (currentStage != mainStage) + { + var prefabStage = PrefabStageUtility.GetPrefabStage(gameObject); + if (prefabStage != null) + { + return true; + } + } + } + return false; + + } + + /// + /// Returns if the Id used is a sceneId already belonging to another object. + /// + /// + /// + private bool IsDuplicateSceneId(ulong id) + { + //Find all nobs in scene. + _sceneNetworkObjects = GameObject.FindObjectsOfType().ToList(); + foreach (NetworkObject nob in _sceneNetworkObjects) + { + if (nob != null && nob != this && nob.SceneId == id) + return true; + } + //If here all checks pass. + return false; + } + + private void ReferenceIds_OnValidate() + { + TryCreateSceneID(); + } + private void ReferenceIds_Reset() + { + TryCreateSceneID(); + } +#endif + } + +} + diff --git a/Assets/FishNet/Runtime/Object/NetworkObject.ReferenceIds.cs.meta b/Assets/FishNet/Runtime/Object/NetworkObject.ReferenceIds.cs.meta new file mode 100644 index 0000000..8858eee --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject.ReferenceIds.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: be0a4b0a32b02f64495ba3b1d22f89c4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/NetworkObject.RpcLinks.cs b/Assets/FishNet/Runtime/Object/NetworkObject.RpcLinks.cs new file mode 100644 index 0000000..2d2269a --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject.RpcLinks.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace FishNet.Object +{ + public sealed partial class NetworkObject : MonoBehaviour + { + + #region Private. + /// + /// RpcLinks being used within this NetworkObject. + /// + private List _rpcLinkIndexes; + #endregion + + /// + /// Sets rpcLinkIndexes to values. + /// + internal void SetRpcLinkIndexes(List values) + { + _rpcLinkIndexes = values; + } + + /// + /// Removes used link indexes from ClientObjects. + /// + internal void RemoveClientRpcLinkIndexes() + { + NetworkManager.ClientManager.Objects.RemoveLinkIndexes(_rpcLinkIndexes); + } + } + +} + diff --git a/Assets/FishNet/Runtime/Object/NetworkObject.RpcLinks.cs.meta b/Assets/FishNet/Runtime/Object/NetworkObject.RpcLinks.cs.meta new file mode 100644 index 0000000..d7142a8 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject.RpcLinks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8b2f6927cf3ef254d91b89e5f99a92b9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/NetworkObject.SyncTypes.cs b/Assets/FishNet/Runtime/Object/NetworkObject.SyncTypes.cs new file mode 100644 index 0000000..3a3e028 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject.SyncTypes.cs @@ -0,0 +1,25 @@ +using UnityEngine; + +namespace FishNet.Object +{ + public sealed partial class NetworkObject : MonoBehaviour + { + /// + /// Writers dirty SyncTypes for all Networkbehaviours if their write tick has been met. + /// + internal void WriteDirtySyncTypes() + { + NetworkBehaviour[] nbs = NetworkBehaviours; + int count = nbs.Length; + for (int i = 0; i < count; i++) + { + //There was a null check here before, shouldn't be needed so it was removed. + NetworkBehaviour nb = nbs[i]; + nb.WriteDirtySyncTypes(true, true); + nb.WriteDirtySyncTypes(false, true); + } + } + } + +} + diff --git a/Assets/FishNet/Runtime/Object/NetworkObject.SyncTypes.cs.meta b/Assets/FishNet/Runtime/Object/NetworkObject.SyncTypes.cs.meta new file mode 100644 index 0000000..a50da6e --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject.SyncTypes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8ffb07b7ea3c5b4419dc5f9941b2d204 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/NetworkObject.cs b/Assets/FishNet/Runtime/Object/NetworkObject.cs new file mode 100644 index 0000000..22447ff --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject.cs @@ -0,0 +1,862 @@ +using FishNet.Managing; +using FishNet.Connection; +using UnityEngine; +using FishNet.Serializing; +using FishNet.Transporting; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using FishNet.Utility.Performance; +using System; +using FishNet.Managing.Object; +using FishNet.Component.Ownership; +#if UNITY_EDITOR +using UnityEditor; +#endif + +namespace FishNet.Object +{ + [DisallowMultipleComponent] + public sealed partial class NetworkObject : MonoBehaviour + { + #region Public. + /// + /// True if was nested during scene setup or within a prefab. + /// + [field: SerializeField, HideInInspector] + public bool IsNested { get; private set; } + /// + /// NetworkConnection which predicted spawned this object. + /// + public NetworkConnection PredictedSpawner { get; private set; } = NetworkManager.EmptyConnection; + /// + /// True if this NetworkObject was active during edit. Will be true if placed in scene during edit, and was in active state on run. + /// + [System.NonSerialized] + internal bool ActiveDuringEdit; + /// + /// Returns if this object was placed in the scene during edit-time. + /// + /// + public bool IsSceneObject => (SceneId > 0); + /// + /// ComponentIndex for this NetworkBehaviour. + /// + [field: SerializeField, HideInInspector] + public byte ComponentIndex { get; private set; } + /// + /// Unique Id for this NetworkObject. This does not represent the object owner. + /// + public int ObjectId { get; private set; } + /// + /// True if this NetworkObject is deinitializing. Will also be true until Initialize is called. May be false until the object is cleaned up if object is destroyed without using Despawn. + /// + internal bool IsDeinitializing { get; private set; } = true; + /// + /// PredictedSpawn component on this object. Will be null if not added manually. + /// + [field: SerializeField, HideInInspector] + public PredictedSpawn PredictedSpawn { get; private set; } + /// + /// + /// + [field: SerializeField, HideInInspector] + private NetworkBehaviour[] _networkBehaviours; + /// + /// NetworkBehaviours within the root and children of this object. + /// + public NetworkBehaviour[] NetworkBehaviours + { + get => _networkBehaviours; + private set => _networkBehaviours = value; + } + /// + /// NetworkObject parenting this instance. The parent NetworkObject will be null if there was no parent during serialization. + /// + [field: SerializeField, HideInInspector] + public NetworkObject ParentNetworkObject { get; private set; } + /// + /// NetworkObjects nested beneath this one. Recursive NetworkObjects may exist within each entry of this field. + /// + [field: SerializeField, HideInInspector] + public List ChildNetworkObjects { get; private set; } = new List(); + /// + /// + /// + [SerializeField, HideInInspector] + internal TransformProperties SerializedTransformProperties = new TransformProperties(); + /// + /// Current state of the NetworkObject. + /// + [System.NonSerialized] + internal NetworkObjectState State = NetworkObjectState.Unset; + #endregion + + #region Serialized. + /// + /// True if the object will always initialize as a networked object. When false the object will not automatically initialize over the network. Using Spawn() on an object will always set that instance as networked. + /// + public bool IsNetworked + { + get => _isNetworked; + private set => _isNetworked = value; + } + /// + /// Sets IsNetworked value. This method must be called before Start. + /// + /// New IsNetworked value. + public void SetIsNetworked(bool value) + { + IsNetworked = value; + } + [Tooltip("True if the object will always initialize as a networked object. When false the object will not automatically initialize over the network. Using Spawn() on an object will always set that instance as networked.")] + [SerializeField] + private bool _isNetworked = true; + /// + /// True to make this object global, and added to the DontDestroyOnLoad scene. This value may only be set for instantiated objects, and can be changed if done immediately after instantiating. + /// + public bool IsGlobal + { + get => _isGlobal; + private set => _isGlobal = value; + } + /// + /// Sets IsGlobal value. + /// + /// New global value. + public void SetIsGlobal(bool value) + { + if (IsNested) + { + NetworkManager.StaticLogWarning($"Object {gameObject.name} cannot change IsGlobal because it is nested. Only root objects may be set global."); + return; + } + if (!IsDeinitializing) + { + NetworkManager.StaticLogWarning($"Object {gameObject.name} cannot change IsGlobal as it's already initialized. IsGlobal may only be changed immediately after instantiating."); + return; + } + if (IsSceneObject) + { + NetworkManager.StaticLogWarning($"Object {gameObject.name} cannot have be global because it is a scene object. Only instantiated objects may be global."); + return; + } + + _networkObserverInitiliazed = false; + IsGlobal = value; + } + [Tooltip("True to make this object global, and added to the DontDestroyOnLoad scene. This value may only be set for instantiated objects, and can be changed if done immediately after instantiating.")] + [SerializeField] + private bool _isGlobal; + /// + /// Order to initialize this object's callbacks when spawned with other NetworkObjects in the same tick. Default value is 0, negative values will execute callbacks first. + /// + public sbyte GetInitializeOrder() => _initializeOrder; + [Tooltip("Order to initialize this object's callbacks when spawned with other NetworkObjects in the same tick. Default value is 0, negative values will execute callbacks first.")] + [SerializeField] + private sbyte _initializeOrder = 0; + /// + /// How to handle this object when it despawns. Scene objects are never destroyed when despawning. + /// + [SerializeField] + [Tooltip("How to handle this object when it despawns. Scene objects are never destroyed when despawning.")] + private DespawnType _defaultDespawnType = DespawnType.Destroy; + /// + /// True to use configured ObjectPool rather than destroy this NetworkObject when being despawned. Scene objects are never destroyed. + /// + public DespawnType GetDefaultDespawnType() => _defaultDespawnType; + /// + /// Sets DespawnType value. + /// + /// Default despawn type for this NetworkObject. + public void SetDefaultDespawnType(DespawnType despawnType) + { + _defaultDespawnType = despawnType; + } + #endregion + + #region Private. + /// + /// True if disabled NetworkBehaviours have been initialized. + /// + private bool _disabledNetworkBehavioursInitialized; + #endregion + + #region Const. + /// + /// Value used when the ObjectId has not been set. + /// + public const int UNSET_OBJECTID_VALUE = ushort.MaxValue; + /// + /// Value used when the PrefabId has not been set. + /// + public const int UNSET_PREFABID_VALUE = ushort.MaxValue; + #endregion + + #region Editor Debug. +#if UNITY_EDITOR + private int _editorOwnerId; +#endif + #endregion + + private void Awake() + { + SetChildDespawnedState(); + } + + private void Start() + { + TryStartDeactivation(); + } + + /// + /// Initializes NetworkBehaviours if they are disabled. + /// + private void InitializeNetworkBehavioursIfDisabled() + { + if (_disabledNetworkBehavioursInitialized) + return; + _disabledNetworkBehavioursInitialized = true; + + for (int i = 0; i < NetworkBehaviours.Length; i++) + NetworkBehaviours[i].InitializeIfDisabled(); + } + + /// + /// Sets Despawned on child NetworkObjects if they are not enabled. + /// + private void SetChildDespawnedState() + { + NetworkObject nob; + for (int i = 0; i < ChildNetworkObjects.Count; i++) + { + nob = ChildNetworkObjects[i]; + if (!nob.gameObject.activeSelf) + nob.State = NetworkObjectState.Despawned; + } + } + + /// + /// Deactivates this NetworkObject during it's start cycle if conditions are met. + /// + internal void TryStartDeactivation() + { + if (!IsNetworked) + return; + + //Global. + if (IsGlobal && !IsSceneObject) + DontDestroyOnLoad(gameObject); + + if (NetworkManager == null || (!NetworkManager.IsClient && !NetworkManager.IsServer)) + { + //ActiveDuringEdit is only used for scene objects. + if (IsSceneObject) + ActiveDuringEdit = true; + gameObject.SetActive(false); + } + } + + private void OnDisable() + { + /* If deinitializing and an owner exist + * then remove object from owner. */ + if (IsDeinitializing && Owner.IsValid) + Owner.RemoveObject(this); + /* If not nested then check to despawn this OnDisable. + * A nob may become disabled without being despawned if it's + * beneath another deinitializing nob. This can be true even while + * not nested because users may move a nob under another at runtime. + * + * This object must also be activeSelf, meaning that it became disabled + * because a parent was. If not activeSelf then it's possible the + * user simply deactivated the object themselves. */ + else if (IsServer && !IsNested && gameObject.activeSelf) + { + bool canDespawn = false; + Transform nextParent = transform.parent; + while (nextParent != null) + { + if (nextParent.TryGetComponent(out NetworkObject pNob)) + { + /* If pNob is not the same as ParentNetworkObject + * then that means this object was moved around. It could be + * that this was previously a child of something else + * or that was given a parent later on in it's life cycle. + ^ + * When this occurs do not send a despawn for this object. + * Rather, let it destroy from unity callbacks which will force + * the proper destroy/stop cycle. */ + if (pNob != ParentNetworkObject) + break; + //If nob is deinitialized then this one cannot exist. + if (pNob.IsDeinitializing) + { + canDespawn = true; + break; + } + } + nextParent = nextParent.parent; + } + + if (canDespawn) + Despawn(); + } + } + + private void OnDestroy() + { + //Does this need to be here? I'm thinking no, remove it and examine later. //todo + if (Owner.IsValid) + Owner.RemoveObject(this); + //Already being deinitialized by FishNet. + if (IsDeinitializing) + return; + + if (NetworkManager != null) + { + //Was destroyed without going through the proper methods. + if (NetworkManager.IsServer) + NetworkManager.ServerManager.Objects.NetworkObjectUnexpectedlyDestroyed(this, true); + if (NetworkManager.IsClient) + NetworkManager.ClientManager.Objects.NetworkObjectUnexpectedlyDestroyed(this, false); + } + + /* When destroyed unexpectedly it's + * impossible to know if this occurred on + * the server or client side, so send callbacks + * for both. */ + if (IsServer) + InvokeStopCallbacks(true); + if (IsClient) + InvokeStopCallbacks(false); + + /* If owner exist then remove object from owner. + * This has to be called here as well OnDisable because + * the OnDisable will only remove the object if + * deinitializing. This is because the object shouldn't + * be removed from owner if the object is simply being + * disabled, but not deinitialized. But in the scenario + * the object is unexpectedly destroyed, which is how we + * arrive here, the object needs to be removed from owner. */ + if (Owner.IsValid) + Owner.RemoveObject(this); + + Observers.Clear(); + IsDeinitializing = true; + + SetActiveStatus(false); + //Do not need to set state if being destroyed. + //Don't need to reset sync types if object is being destroyed. + } + + /// + /// Sets IsClient or IsServer to isActive. + /// + private void SetActiveStatus(bool isActive, bool server) + { + if (server) + IsServer = isActive; + else + IsClient = isActive; + } + /// + /// Sets IsClient and IsServer to isActive. + /// + private void SetActiveStatus(bool isActive) + { + IsServer = isActive; + IsClient = isActive; + } + /// + /// Initializes this script. This is only called once even when as host. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void Preinitialize_Internal(NetworkManager networkManager, int objectId, NetworkConnection owner, bool asServer) + { + State = NetworkObjectState.Spawned; + InitializeNetworkBehavioursIfDisabled(); + IsDeinitializing = false; + //QOL references. + NetworkManager = networkManager; + ServerManager = networkManager.ServerManager; + ClientManager = networkManager.ClientManager; + ObserverManager = networkManager.ObserverManager; + TransportManager = networkManager.TransportManager; + TimeManager = networkManager.TimeManager; + SceneManager = networkManager.SceneManager; + PredictionManager = networkManager.PredictionManager; + RollbackManager = networkManager.RollbackManager; + + SetOwner(owner); + ObjectId = objectId; + + /* This must be called at the beginning + * so that all conditions are handled by the observer + * manager prior to the preinitialize call on networkobserver. + * The method called is dependent on NetworkManager being set. */ + AddDefaultNetworkObserverConditions(); + + for (int i = 0; i < NetworkBehaviours.Length; i++) + NetworkBehaviours[i].InitializeOnce_Internal(); + + /* NetworkObserver uses some information from + * NetworkBehaviour so it must be preinitialized + * after NetworkBehaviours are. */ + if (asServer) + NetworkObserver.PreInitialize(this); + _networkObserverInitiliazed = true; + + //Add to connection objects if owner exist. + if (owner != null) + owner.AddObject(this); + } + + /// + /// Adds a NetworkBehaviour and serializes it's components. + /// + internal T AddAndSerialize() where T : NetworkBehaviour //runtimeNB make public. + { + int startingLength = NetworkBehaviours.Length; + T result = gameObject.AddComponent(); + //Add to network behaviours. + Array.Resize(ref _networkBehaviours, startingLength + 1); + _networkBehaviours[startingLength] = result; + //Serialize values and return. + result.SerializeComponents(this, (byte)startingLength); + return result; + } + + /// + /// Updates NetworkBehaviours and initializes them with serialized values. + /// + /// True if this call originated from a prefab collection, such as during it's initialization. + internal void UpdateNetworkBehaviours(NetworkObject parentNob, ref byte componentIndex) //runtimeNB make public. + { + /* This method can be called by the developer initializing prefabs, the prefab collection doing it automatically, + * or when the networkobject is modified or added to an object. + * + * Prefab collections generally contain all prefabs, meaning they will not only call this on the topmost + * networkobject but also each child, as the child would be it's own prefab in the collection. This assumes + * that is, the child is a nested prefab. + * + * Because of this potential a check must be done where if the componentIndex is 0 we must look + * for a networkobject above this one. If there is a networkObject above this one then we know the prefab + * is being initialized individually, not part of a recursive check. In this case exit early + * as the parent would have already resolved the needed information. */ + + //If first componentIndex make sure there's no more than maximum allowed nested nobs. + if (componentIndex == 0) + { + //Not possible for index to be 0 and nested. + if (IsNested) + return; + byte maxNobs = 255; + if (GetComponentsInChildren(true).Length > maxNobs) + { + Debug.LogError($"The number of child NetworkObjects on {gameObject.name} exceeds the maximum of {maxNobs}."); + return; + } + } + + PredictedSpawn = GetComponent(); + ComponentIndex = componentIndex; + ParentNetworkObject = parentNob; + + //Transforms which can be searched for networkbehaviours. + ListCache transformCache = ListCaches.GetTransformCache(); + transformCache.Reset(); + ChildNetworkObjects.Clear(); + + transformCache.AddValue(transform); + for (int z = 0; z < transformCache.Written; z++) + { + Transform currentT = transformCache.Collection[z]; + for (int i = 0; i < currentT.childCount; i++) + { + Transform t = currentT.GetChild(i); + /* If contains a nob then do not add to transformsCache. + * Do add to ChildNetworkObjects so it can be initialized when + * parent is. */ + if (t.TryGetComponent(out NetworkObject childNob)) + { + /* Make sure both objects have the same value for + * IsSceneObject. It's possible the user instantiated + * an object and placed it beneath a scene object + * before the scene initialized. They may also + * add a scene object under an instantiated, even though + * this almost certainly will break things. */ + if (IsSceneObject == childNob.IsSceneObject) + ChildNetworkObjects.Add(childNob); + } + else + { + transformCache.AddValue(t); + } + } + } + + int written; + //Iterate all cached transforms and get networkbehaviours. + ListCache nbCache = ListCaches.GetNetworkBehaviourCache(); + nbCache.Reset(); + written = transformCache.Written; + List ts = transformCache.Collection; + // + for (int i = 0; i < written; i++) + nbCache.AddValues(ts[i].GetNetworkBehaviours()); + + //Copy to array. + written = nbCache.Written; + List nbs = nbCache.Collection; + NetworkBehaviours = new NetworkBehaviour[written]; + // + for (int i = 0; i < written; i++) + { + NetworkBehaviours[i] = nbs[i]; + NetworkBehaviours[i].SerializeComponents(this, (byte)i); + } + + ListCaches.StoreCache(transformCache); + ListCaches.StoreCache(nbCache); + + //Tell children nobs to update their NetworkBehaviours. + foreach (NetworkObject item in ChildNetworkObjects) + { + componentIndex++; + item.UpdateNetworkBehaviours(this, ref componentIndex); + } + } + + /// + /// Called after all data is synchronized with this NetworkObject. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void Initialize(bool asServer, bool invokeSyncTypeCallbacks) + { + InitializeCallbacks(asServer, invokeSyncTypeCallbacks); + } + + /// + /// Called to prepare this object to be destroyed or disabled. + /// + internal void Deinitialize(bool asServer) + { + InvokeStopCallbacks(asServer); + if (asServer) + { + IsDeinitializing = true; + } + else + { + ClientManager.Connection.LevelOfDetails.Remove(this); + //Client only. + if (!NetworkManager.IsServer) + IsDeinitializing = true; + + RemoveClientRpcLinkIndexes(); + } + + SetActiveStatus(false, asServer); + if (asServer) + Observers.Clear(); + } + + /// + /// Resets states for object to be pooled. + /// + /// True if performing as server. + public void ResetForObjectPool() + { + int count = NetworkBehaviours.Length; + for (int i = 0; i < count; i++) + NetworkBehaviours[i].ResetForObjectPool(); + + State = NetworkObjectState.Unset; + SetOwner(NetworkManager.EmptyConnection); + NetworkObserver.Deinitialize(); + //QOL references. + NetworkManager = null; + ServerManager = null; + ClientManager = null; + ObserverManager = null; + TransportManager = null; + TimeManager = null; + SceneManager = null; + RollbackManager = null; + //Misc sets. + ObjectId = 0; + ClientInitialized = false; + } + + /// + /// Removes ownership from all clients. + /// + public void RemoveOwnership() + { + GiveOwnership(null, true); + } + /// + /// Gives ownership to newOwner. + /// + /// + public void GiveOwnership(NetworkConnection newOwner) + { + GiveOwnership(newOwner, true); + } + /// + /// Gives ownership to newOwner. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void GiveOwnership(NetworkConnection newOwner, bool asServer) + { + /* Additional asServer checks. */ + if (asServer) + { + if (!NetworkManager.IsServer) + { + NetworkManager.LogWarning($"Ownership cannot be given for object {gameObject.name}. Only server may give ownership."); + return; + } + + //If the same owner don't bother sending a message, just ignore request. + if (newOwner == Owner && asServer) + return; + + if (newOwner != null && newOwner.IsActive && !newOwner.LoadedStartScenes(true)) + { + NetworkManager.LogWarning($"Ownership has been transfered to ConnectionId {newOwner.ClientId} but this is not recommended until after they have loaded start scenes. You can be notified when a connection loads start scenes by using connection.OnLoadedStartScenes on the connection, or SceneManager.OnClientLoadStartScenes."); + } + } + + bool activeNewOwner = (newOwner != null && newOwner.IsActive); + + //Set prevOwner, disallowing null. + NetworkConnection prevOwner = Owner; + if (prevOwner == null) + prevOwner = NetworkManager.EmptyConnection; + + SetOwner(newOwner); + /* Only modify objects if asServer or not + * host. When host, server would + * have already modified objects + * collection so there is no need + * for client to as well. */ + if (asServer || !NetworkManager.IsHost) + { + if (activeNewOwner) + newOwner.AddObject(this); + if (prevOwner.IsValid && prevOwner != newOwner) + prevOwner.RemoveObject(this); + } + + //After changing owners invoke callbacks. + InvokeOwnership(prevOwner, asServer); + + //If asServer send updates to clients as needed. + if (asServer) + { + if (activeNewOwner) + ServerManager.Objects.RebuildObservers(this, newOwner); + + using (PooledWriter writer = WriterPool.GetWriter()) + { + writer.WritePacketId(PacketId.OwnershipChange); + writer.WriteNetworkObject(this); + writer.WriteNetworkConnection(Owner); + //If sharing then send to all observers. + if (NetworkManager.ServerManager.ShareIds) + { + NetworkManager.TransportManager.SendToClients((byte)Channel.Reliable, writer.GetArraySegment(), this); + } + //Only sending to old / new. + else + { + if (prevOwner.IsActive) + NetworkManager.TransportManager.SendToClient((byte)Channel.Reliable, writer.GetArraySegment(), prevOwner); + if (activeNewOwner) + NetworkManager.TransportManager.SendToClient((byte)Channel.Reliable, writer.GetArraySegment(), newOwner); + } + } + + if (prevOwner.IsActive) + ServerManager.Objects.RebuildObservers(prevOwner); + } + } + + + /// + /// Initializes a predicted object for client. + /// + internal void InitializePredictedObject_Server(NetworkManager manager, NetworkConnection predictedSpawner) + { + NetworkManager = manager; + PredictedSpawner = predictedSpawner; + } + + + /// + /// Initializes a predicted object for client. + /// + internal void PreinitializePredictedObject_Client(NetworkManager manager, int objectId, NetworkConnection owner, NetworkConnection predictedSpawner) + { + PredictedSpawner = predictedSpawner; + Preinitialize_Internal(manager, objectId, owner, false); + } + + /// + /// Deinitializes this predicted spawned object. + /// + internal void DeinitializePredictedObject_Client() + { + /* For the time being we're just going to disable the object because + * deinitializing instead could present a lot of problems. + * For example: if client deinitializes rpc links are unregistered, + * and if server had a rpc on the way already the link would + * not be found. This would cause the reader length to be wrong + * resulting in packet corruption. */ + gameObject.SetActive(false); + } + + /// + /// Sets the owner of this object. + /// + /// + /// + private void SetOwner(NetworkConnection owner) + { + Owner = owner; + } + + /// + /// Returns if this NetworkObject is a scene object, and has changed. + /// + /// + internal ChangedTransformProperties GetTransformChanges(TransformProperties stp) + { + ChangedTransformProperties ctp = ChangedTransformProperties.Unset; + if (transform.localPosition != stp.Position) + ctp |= ChangedTransformProperties.LocalPosition; + if (transform.localRotation != stp.Rotation) + ctp |= ChangedTransformProperties.LocalRotation; + if (transform.localScale != stp.LocalScale) + ctp |= ChangedTransformProperties.LocalScale; + + return ctp; + } + + /// + /// Returns if this NetworkObject is a scene object, and has changed. + /// + /// + internal ChangedTransformProperties GetTransformChanges(GameObject prefab) + { + Transform t = prefab.transform; + ChangedTransformProperties ctp = ChangedTransformProperties.Unset; + if (transform.position != t.position) + ctp |= ChangedTransformProperties.LocalPosition; + if (transform.rotation != t.rotation) + ctp |= ChangedTransformProperties.LocalRotation; + if (transform.localScale != t.localScale) + ctp |= ChangedTransformProperties.LocalScale; + + return ctp; + } + + #region Editor. +#if UNITY_EDITOR + /// + /// Removes duplicate NetworkObject components on this object returning the removed count. + /// + /// + internal int RemoveDuplicateNetworkObjects() + { + NetworkObject[] nobs = GetComponents(); + for (int i = 1; i < nobs.Length; i++) + DestroyImmediate(nobs[i]); + + return (nobs.Length - 1); + } + + /// + /// Sets IsNested and returns the result. + /// + /// + private bool SetIsNestedThroughTraversal() + { + Transform parent = transform.parent; + //Iterate long as parent isn't null, and isnt self. + while (parent != null && parent != transform) + { + if (parent.TryGetComponent(out _)) + { + IsNested = true; + return IsNested; + } + + parent = parent.parent; + } + + //No NetworkObject found in parents, meaning this is not nested. + IsNested = false; + return IsNested; + } + + private void OnValidate() + { + SetIsNestedThroughTraversal(); + SceneUpdateNetworkBehaviours(); + ReferenceIds_OnValidate(); + + if (IsGlobal && IsSceneObject) + Debug.LogWarning($"Object {gameObject.name} will have it's IsGlobal state ignored because it is a scene object. Instantiated copies will still be global. This warning is informative only."); + } + + private void Reset() + { + SetIsNestedThroughTraversal(); + SerializeTransformProperties(); + SceneUpdateNetworkBehaviours(); + ReferenceIds_Reset(); + } + + private void SceneUpdateNetworkBehaviours() + { + //In a scene. + if (!string.IsNullOrEmpty(gameObject.scene.name)) + { + if (IsNested) + return; + + byte componentIndex = 0; + UpdateNetworkBehaviours(null, ref componentIndex); + } + + } + private void OnDrawGizmosSelected() + { + _editorOwnerId = (Owner == null) ? -1 : Owner.ClientId; + SerializeTransformProperties(); + } + + /// + /// Serializes TransformProperties to current transform properties. + /// + private void SerializeTransformProperties() + { + /* Use this method to set scene data since it doesn't need to exist outside + * the editor and because its updated regularly while selected. */ + //If a scene object. + if (!EditorApplication.isPlaying && !string.IsNullOrEmpty(gameObject.scene.name)) + { + SerializedTransformProperties = new TransformProperties( + transform.localPosition, transform.localRotation, transform.localScale); + } + } +#endif + #endregion + } + +} + diff --git a/Assets/FishNet/Runtime/Object/NetworkObject.cs.meta b/Assets/FishNet/Runtime/Object/NetworkObject.cs.meta new file mode 100644 index 0000000..228ed52 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 26b716c41e9b56b4baafaf13a523ba2e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/NetworkObjectState.cs b/Assets/FishNet/Runtime/Object/NetworkObjectState.cs new file mode 100644 index 0000000..0b0b6d1 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObjectState.cs @@ -0,0 +1,24 @@ + +namespace FishNet.Object +{ + /// + /// Current state of the NetworkObject. + /// + internal enum NetworkObjectState : byte + { + /// + /// State has not been set. This occurs when the object has never been spawned or despawned. + /// + Unset = 0, + /// + /// Object is currently spawned. + /// + Spawned = 1, + /// + /// Object is currently despawned. + /// + Despawned = 2, + } + +} + diff --git a/Assets/FishNet/Runtime/Object/NetworkObjectState.cs.meta b/Assets/FishNet/Runtime/Object/NetworkObjectState.cs.meta new file mode 100644 index 0000000..1873ca3 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObjectState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 536f137f11dc6654eab9fbe94ca14cd8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/PredictedSpawningType.cs b/Assets/FishNet/Runtime/Object/PredictedSpawningType.cs new file mode 100644 index 0000000..43ad004 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/PredictedSpawningType.cs @@ -0,0 +1,14 @@ +using FishNet.Object.Helping; + +namespace FishNet.Object +{ + [System.Flags] + public enum PredictedSpawningType : byte + { + Disabled = 0, + Spawn = 1, + Despawn = 2, + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/PredictedSpawningType.cs.meta b/Assets/FishNet/Runtime/Object/PredictedSpawningType.cs.meta new file mode 100644 index 0000000..59f8d22 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/PredictedSpawningType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a4b112cfcb8cd57429eb0a4a32c5a774 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Prediction.meta b/Assets/FishNet/Runtime/Object/Prediction.meta new file mode 100644 index 0000000..a673566 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8bce6e27e176a2d4d91783beffe40ac2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Prediction/Attributes.cs b/Assets/FishNet/Runtime/Object/Prediction/Attributes.cs new file mode 100644 index 0000000..f3d355c --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/Attributes.cs @@ -0,0 +1,29 @@ +using System; + +namespace FishNet.Object.Prediction +{ + /// + /// Replicated methods are to be called from clients and will run the same data and logic on the server. + /// Only data used as method arguments will be serialized. + /// + [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)] + public class ReplicateAttribute : Attribute + { + /// + /// How many past datas to resend. + /// + public byte Resends = 5; + } + /// + /// Reconcile methods indicate how to reset your script or object after the server has replicated user data. + /// + [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)] + public class ReconcileAttribute : Attribute + { + /// + /// How many times to resend reconcile. + /// + public byte Resends = 3; + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Prediction/Attributes.cs.meta b/Assets/FishNet/Runtime/Object/Prediction/Attributes.cs.meta new file mode 100644 index 0000000..fdd81b7 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/Attributes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b082d36535ce0404d8438bc1b0499e53 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Prediction/Delegates.cs b/Assets/FishNet/Runtime/Object/Prediction/Delegates.cs new file mode 100644 index 0000000..4b884c5 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/Delegates.cs @@ -0,0 +1,20 @@ +using FishNet.Connection; +using FishNet.Documenting; +using FishNet.Serializing; +using FishNet.Transporting; +using FishNet.Utility.Constant; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo(UtilityConstants.CODEGEN_ASSEMBLY_NAME)] +namespace FishNet.Object.Prediction.Delegating +{ + [APIExclude] + public delegate void ReplicateRpcDelegate(PooledReader reader, NetworkConnection sender, Channel channel); + [APIExclude] + public delegate void ReconcileRpcDelegate(PooledReader reader, Channel channel); + + [APIExclude] + public delegate void ReplicateUserLogicDelegate(T data, bool asServer, Channel channel, bool replaying); + [APIExclude] + public delegate void ReconcileUserLogicDelegate(T data, bool asServer, Channel channel); +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Prediction/Delegates.cs.meta b/Assets/FishNet/Runtime/Object/Prediction/Delegates.cs.meta new file mode 100644 index 0000000..d3c53db --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/Delegates.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c9904192dacd41a4ba7b29bc3199ec3a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Prediction/Interfaces.cs b/Assets/FishNet/Runtime/Object/Prediction/Interfaces.cs new file mode 100644 index 0000000..6c9cb31 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/Interfaces.cs @@ -0,0 +1,18 @@ + +namespace FishNet.Object.Prediction +{ + public interface IReplicateData + { + uint GetTick(); + void SetTick(uint value); + void Dispose(); + } + + public interface IReconcileData + { + uint GetTick(); + void SetTick(uint value); + void Dispose(); + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Prediction/Interfaces.cs.meta b/Assets/FishNet/Runtime/Object/Prediction/Interfaces.cs.meta new file mode 100644 index 0000000..24909d5 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/Interfaces.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 515754257f85574438408c7f5b268590 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/RpcLinkType.cs b/Assets/FishNet/Runtime/Object/RpcLinkType.cs new file mode 100644 index 0000000..84f89f4 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/RpcLinkType.cs @@ -0,0 +1,25 @@ +using FishNet.Object.Helping; + +namespace FishNet.Object +{ + + + internal struct RpcLinkType + { + /// + /// Index of link. + /// + public ushort LinkIndex; + /// + /// Type of Rpc link is for. + /// + public RpcType RpcType; + + public RpcLinkType(ushort linkIndex, RpcType rpcType) + { + LinkIndex = linkIndex; + RpcType = rpcType; + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/RpcLinkType.cs.meta b/Assets/FishNet/Runtime/Object/RpcLinkType.cs.meta new file mode 100644 index 0000000..3781ab0 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/RpcLinkType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4fa68ca6a21d08f42980dcf68f984d53 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/SyncTypeWriteType.cs b/Assets/FishNet/Runtime/Object/SyncTypeWriteType.cs new file mode 100644 index 0000000..c5c68b9 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/SyncTypeWriteType.cs @@ -0,0 +1,12 @@ +namespace FishNet.Object +{ + + internal enum SyncTypeWriteType + { + Observers = 0, + Owner = 1, + All = 2, + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/SyncTypeWriteType.cs.meta b/Assets/FishNet/Runtime/Object/SyncTypeWriteType.cs.meta new file mode 100644 index 0000000..2d119c2 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/SyncTypeWriteType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e6406cc7d5fe47c44a26298145f54b00 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Synchronizing.meta b/Assets/FishNet/Runtime/Object/Synchronizing.meta new file mode 100644 index 0000000..fd7d57a --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 340de3ebc97a18642be780cbfeda01dc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/ICustomSync.cs b/Assets/FishNet/Runtime/Object/Synchronizing/ICustomSync.cs new file mode 100644 index 0000000..71ed36b --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/ICustomSync.cs @@ -0,0 +1,19 @@ + +namespace FishNet.Object.Synchronizing +{ + /// + /// Custom SyncObjects must inherit from SyncBase and implement this interface. + /// + public interface ICustomSync + { + /// + /// Get the serialized type. + /// This must return the value type you are synchronizing, for example a struct or class. + /// If you are not synchronizing a particular value but instead of supported values such as int, bool, ect, then you may return null on this method. + /// + /// + object GetSerializedType(); + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/ICustomSync.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/ICustomSync.cs.meta new file mode 100644 index 0000000..7789232 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/ICustomSync.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2024b0be0cd1cc744a442f3e2e6ba483 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/ISyncObject.cs b/Assets/FishNet/Runtime/Object/Synchronizing/ISyncObject.cs new file mode 100644 index 0000000..9356bbd --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/ISyncObject.cs @@ -0,0 +1,48 @@ +using FishNet.Managing; +using FishNet.Serializing; +using System; + +namespace FishNet.Object.Synchronizing.Internal +{ + /// + /// A sync object is an object that can synchronize it's state + /// between server and client, such as a SyncList + /// + public interface ISyncType + { + /// + /// true if there are changes since the last flush + /// + bool IsDirty { get; } + /// + /// Sets index for the SyncType. + /// + void SetRegistered(); + /// + /// PreInitializes this for use with the network. + /// + void PreInitialize(NetworkManager networkManager); + /// + /// Writes all changed values. + /// + /// + ///True to set the next time data may sync. + void WriteDelta(PooledWriter writer, bool resetSyncTick = true); + /// + /// Writers all values if not initial values. + /// + /// + void WriteFull(PooledWriter writer); + /// + /// Sets current values. + /// + /// + void Read(PooledReader reader); + /// + /// Resets the SyncObject so that it can be re-used + /// + void Reset(); + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/ISyncObject.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/ISyncObject.cs.meta new file mode 100644 index 0000000..e353a5a --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/ISyncObject.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9d0e81c03149ecd4eba926bba2d0edbe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/MissingObjectPacketLength.cs b/Assets/FishNet/Runtime/Object/Synchronizing/MissingObjectPacketLength.cs new file mode 100644 index 0000000..56e0d3d --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/MissingObjectPacketLength.cs @@ -0,0 +1,11 @@ +namespace FishNet.Object +{ + + internal enum MissingObjectPacketLength : int + { + Reliable = -1, + PurgeRemaiming = -2, + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/MissingObjectPacketLength.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/MissingObjectPacketLength.cs.meta new file mode 100644 index 0000000..68a542f --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/MissingObjectPacketLength.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3d177496f9519e246b8e3ef199d83437 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/ReadPermissions.cs b/Assets/FishNet/Runtime/Object/Synchronizing/ReadPermissions.cs new file mode 100644 index 0000000..73c2afd --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/ReadPermissions.cs @@ -0,0 +1,21 @@ +namespace FishNet.Object.Synchronizing +{ + /// + /// Which clients may receive synchronization updates. + /// + public enum ReadPermission : byte + { + /// + /// All observers will receive updates. + /// + Observers, + /// + /// Only owner will receive updates. + /// + OwnerOnly, + /// + /// Send to all observers except owner. + /// + ExcludeOwner + } +} diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/ReadPermissions.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/ReadPermissions.cs.meta new file mode 100644 index 0000000..e123b96 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/ReadPermissions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8050ef114e01f74409d8e29b821b6fc0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SecretMenu.meta b/Assets/FishNet/Runtime/Object/Synchronizing/SecretMenu.meta new file mode 100644 index 0000000..28aeb41 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SecretMenu.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8c38d8297956cf34c9826ebe5fbadff6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SecretMenu/SyncVarExtensions.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SecretMenu/SyncVarExtensions.cs new file mode 100644 index 0000000..b318742 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SecretMenu/SyncVarExtensions.cs @@ -0,0 +1,21 @@ +//using FishNet.Documenting; + +//Remove on 2023/06/01 +//namespace FishNet.Object.Synchronizing.SecretMenu +//{ +// /// +// /// Internal SyncVar extensions. +// /// +// [APIExclude] +// public static class SyncVarExtensions +// { +// /// +// /// Dirties SyncVars. +// /// +// /// +// [APIExclude] +// public static void Dirty(this object obj) { } +// } + + +//} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SecretMenu/SyncVarExtensions.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/SecretMenu/SyncVarExtensions.cs.meta new file mode 100644 index 0000000..aa9b282 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SecretMenu/SyncVarExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d1f982cd721eb344c88bab105d779127 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/Settings.cs b/Assets/FishNet/Runtime/Object/Synchronizing/Settings.cs new file mode 100644 index 0000000..5f610bd --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/Settings.cs @@ -0,0 +1,53 @@ +using FishNet.Transporting; + +namespace FishNet.Object.Synchronizing.Internal +{ + public class Settings + { + /// + /// Defines the write permissions for this var + /// + public WritePermission WritePermission = WritePermission.ServerOnly; + /// + /// Clients which may receive updated values. + /// + public ReadPermission ReadPermission = ReadPermission.Observers; + /// + /// How often this variable may synchronize. + /// + public float SendRate = 0f; + /// + /// Channel to send values on. + /// + public Channel Channel = Channel.Reliable; + + /// + /// Constructs a new NetworkedVarSettings instance + /// + public Settings() + { + + } + + public Settings(WritePermission writePermission, ReadPermission readPermission, float sendRate, Channel channel) + { + WritePermission = writePermission; + ReadPermission = readPermission; + SendRate = sendRate; + Channel = channel; + } + + public Settings(float sendTickrate) + { + SendRate = sendTickrate; + } + + public Settings(ReadPermission readPermission, float sendRate, Channel channel) + { + ReadPermission = readPermission; + SendRate = sendRate; + Channel = channel; + } + + } +} diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/Settings.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/Settings.cs.meta new file mode 100644 index 0000000..2492a21 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/Settings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 24688925098da4d42be8dbfa66b88b82 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncBase.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncBase.cs new file mode 100644 index 0000000..7dc53f5 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncBase.cs @@ -0,0 +1,265 @@ +using FishNet.Managing; +using FishNet.Managing.Timing; +using FishNet.Serializing; +using FishNet.Transporting; +using System; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Object.Synchronizing.Internal +{ + public class SyncBase : ISyncType + { + + #region Public. + /// + /// True if this SyncBase has been registered within it's containing class. + /// + public bool IsRegistered { get; private set; } + /// + /// True if the object for which this SyncType is for has been initialized for the network. + /// + public bool IsNetworkInitialized => (IsRegistered && (NetworkBehaviour.IsServer || NetworkBehaviour.IsClient)); + /// + /// True if a SyncObject, false if a SyncVar. + /// + public bool IsSyncObject { get; private set; } + /// + /// The settings for this SyncVar. + /// + public Settings Settings = new Settings(); + /// + /// How often updates may send. + /// + public float SendRate => Settings.SendRate; + /// + /// True if this SyncVar needs to send data. + /// + public bool IsDirty { get; private set; } + /// + /// NetworkManager this uses. + /// + public NetworkManager NetworkManager = null; + /// + /// NetworkBehaviour this SyncVar belongs to. + /// + public NetworkBehaviour NetworkBehaviour = null; + /// + /// Next time a SyncVar may send data/ + /// + public uint NextSyncTick = 0; + /// + /// Index within the sync collection. + /// + public uint SyncIndex { get; protected set; } = 0; + /// + /// Channel to send on. + /// + internal Channel Channel => _currentChannel; + #endregion + + #region Private. + /// + /// Sync interval converted to ticks. + /// + private uint _timeToTicks; + /// + /// Channel to use for next write. To ensure eventual consistency this eventually changes to reliable when Settings are unreliable. + /// + private Channel _currentChannel; + #endregion + + /// + /// Initializes this SyncBase. + /// + public void InitializeInstance(NetworkBehaviour nb, uint syncIndex, WritePermission writePermissions, ReadPermission readPermissions, float tickRate, Channel channel, bool isSyncObject) + { + NetworkBehaviour = nb; + SyncIndex = syncIndex; + _currentChannel = channel; + IsSyncObject = isSyncObject; + Settings = new Settings() + { + WritePermission = writePermissions, + ReadPermission = readPermissions, + SendRate = tickRate, + Channel = channel + }; + + NetworkBehaviour.RegisterSyncType(this, SyncIndex); + } + + /// + /// Sets the SyncIndex. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetRegistered() + { + Registered(); + } + + /// + /// Called when the SyncType has been registered, but not yet initialized over the network. + /// + protected virtual void Registered() + { + IsRegistered = true; + } + + /// + /// PreInitializes this for use with the network. + /// + public void PreInitialize(NetworkManager networkManager) + { + NetworkManager = networkManager; + if (Settings.SendRate < 0f) + Settings.SendRate = networkManager.ServerManager.GetSynctypeRate(); + + _timeToTicks = NetworkManager.TimeManager.TimeToTicks(Settings.SendRate, TickRounding.RoundUp); + } + + /// + /// Called after OnStartXXXX has occurred. + /// + /// True if OnStartServer was called, false if OnStartClient. + public virtual void OnStartCallback(bool asServer) { } + + protected bool CanNetworkSetValues(bool warn = true) + { + /* If not registered then values can be set + * since at this point the object is still being initialized + * in awake so we want those values to be applied. */ + if (!IsRegistered) + return true; + /* If the network is not initialized yet then let + * values be set. Values set here will not synchronize + * to the network. We are assuming the user is setting + * these values on client and server appropriately + * since they are being applied prior to this object + * being networked. */ + if (!IsNetworkInitialized) + return true; + //If server is active then values can be set no matter what. + if (NetworkBehaviour.IsServer) + return true; + //Predicted spawning is enabled. + if (NetworkManager != null && NetworkManager.PredictionManager.GetAllowPredictedSpawning() && NetworkBehaviour.NetworkObject.AllowPredictedSpawning) + return true; + /* If here then server is not active and additional + * checks must be performed. */ + bool result = (Settings.ReadPermission == ReadPermission.ExcludeOwner && NetworkBehaviour.IsOwner); + if (!result && warn) + LogServerNotActiveWarning(); + + return result; + } + + /// + /// Logs that the operation could not be completed because the server is not active. + /// + protected void LogServerNotActiveWarning() + { + if (NetworkManager != null) + NetworkManager.LogWarning($"Cannot complete operation as server when server is not active."); + } + + /// + /// Dirties this Sync and the NetworkBehaviour. + /// + public bool Dirty() + { + /* Reset channel even if already dirty. + * This is because the value might have changed + * which will reset the eventual consistency state. */ + _currentChannel = Settings.Channel; + + /* Once dirty don't undirty until it's + * processed. This ensures that data + * is flushed. */ + bool canDirty = NetworkBehaviour.DirtySyncType(IsSyncObject); + IsDirty |= canDirty; + + return canDirty; + } + + /// + /// Sets IsDirty to false. + /// + internal void ResetDirty() + { + //If not a sync object and using unreliable channel. + if (!IsSyncObject && Settings.Channel == Channel.Unreliable) + { + //Check if dirty can be unset or if another tick must be run using reliable. + if (_currentChannel == Channel.Unreliable) + _currentChannel = Channel.Reliable; + //Already sent reliable, can undirty. Channel will reset next time this dirties. + else + IsDirty = false; + } + //If syncObject or using reliable unset dirty. + else + { + IsDirty = false; + } + } + /// + /// True if dirty and enough time has passed to write changes. + /// + /// + /// + internal bool WriteTimeMet(uint tick) + { + return (IsDirty && tick >= NextSyncTick); + } + /// + /// Writes current value. + /// + /// + /// True to set the next time data may sync. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void WriteDelta(PooledWriter writer, bool resetSyncTick = true) + { + WriteHeader(writer, resetSyncTick); + } + /// + /// Writers the header for this SyncType. + /// + /// + /// + protected virtual void WriteHeader(PooledWriter writer, bool resetSyncTick = true) + { + if (resetSyncTick) + NextSyncTick = NetworkManager.TimeManager.Tick + _timeToTicks; + + writer.WriteByte((byte)SyncIndex); + } + /// + /// Writes current value if not initialized value. + /// + /// + public virtual void WriteFull(PooledWriter writer) { } + /// + /// Sets current value as client. + /// + /// + [Obsolete("Use Read(PooledReader, bool).")] + public virtual void Read(PooledReader reader) { } + /// + /// Sets current value as server or client. + /// + /// + /// + public virtual void Read(PooledReader reader, bool asServer) { } + /// + /// Resets to initialized values. + /// + public virtual void Reset() + { + NextSyncTick = 0; + ResetDirty(); + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncBase.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/SyncBase.cs.meta new file mode 100644 index 0000000..9fb0dec --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1a6f26e3f8016cc499b3fa99e7368fbc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs new file mode 100644 index 0000000..db2c8db --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs @@ -0,0 +1,628 @@ +using FishNet.Documenting; +using FishNet.Managing.Logging; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using FishNet.Utility.Extension; +using JetBrains.Annotations; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Object.Synchronizing +{ + + public class SyncIDictionary : SyncBase, IDictionary, IReadOnlyDictionary + { + + #region Types. + /// + /// Information needed to invoke a callback. + /// + private struct CachedOnChange + { + internal readonly SyncDictionaryOperation Operation; + internal readonly TKey Key; + internal readonly TValue Value; + + public CachedOnChange(SyncDictionaryOperation operation, TKey key, TValue value) + { + Operation = operation; + Key = key; + Value = value; + } + } + + /// + /// Information about how the collection has changed. + /// + private struct ChangeData + { + internal readonly SyncDictionaryOperation Operation; + internal readonly TKey Key; + internal readonly TValue Value; + + public ChangeData(SyncDictionaryOperation operation, TKey key, TValue value) + { + this.Operation = operation; + this.Key = key; + this.Value = value; + } + } + #endregion + + #region Public. + /// + /// Implementation from Dictionary. Not used. + /// + [APIExclude] + public bool IsReadOnly => false; + /// + /// Delegate signature for when SyncDictionary changes. + /// + /// Operation being completed, such as Add, Set, Remove. + /// Key being modified. + /// Value of operation. + /// True if callback is on the server side. False is on the client side. + [APIExclude] + public delegate void SyncDictionaryChanged(SyncDictionaryOperation op, TKey key, TValue value, bool asServer); + /// + /// Called when the SyncDictionary changes. + /// + public event SyncDictionaryChanged OnChange; + /// + /// Collection of objects. + /// + public readonly IDictionary Collection; + /// + /// Copy of objects on client portion when acting as a host. + /// + public readonly IDictionary ClientHostCollection = new Dictionary(); + /// + /// Number of objects in the collection. + /// + public int Count => Collection.Count; + /// + /// Keys within the collection. + /// + public ICollection Keys => Collection.Keys; + [APIExclude] + IEnumerable IReadOnlyDictionary.Keys => Collection.Keys; + /// + /// Values within the collection. + /// + public ICollection Values => Collection.Values; + [APIExclude] + IEnumerable IReadOnlyDictionary.Values => Collection.Values; + #endregion + + #region Private. + /// + /// Initial values for the dictionary. + /// + private IDictionary _initialValues = new Dictionary(); + /// + /// Changed data which will be sent next tick. + /// + private readonly List _changed = new List(); + /// + /// Server OnChange events waiting for start callbacks. + /// + private readonly List _serverOnChanges = new List(); + /// + /// Client OnChange events waiting for start callbacks. + /// + private readonly List _clientOnChanges = new List(); + /// + /// True if values have changed since initialization. + /// The only reasonable way to reset this during a Reset call is by duplicating the original list and setting all values to it on reset. + /// + private bool _valuesChanged; + /// + /// True to send all values in the next WriteDelta. + /// + private bool _sendAll; + #endregion + + [APIExclude] + public SyncIDictionary(IDictionary objects) + { + this.Collection = objects; + //Add to clienthostcollection. + foreach (KeyValuePair item in objects) + this.ClientHostCollection[item.Key] = item.Value; + } + + /// + /// Gets the collection being used within this SyncList. + /// + /// True if returning the server value, false if client value. The values will only differ when running as host. While asServer is true the most current values on server will be returned, and while false the latest values received by client will be returned. + /// The used collection. + public Dictionary GetCollection(bool asServer) + { + bool asClientAndHost = (!asServer && base.NetworkManager.IsServer); + IDictionary collection = (asClientAndHost) ? ClientHostCollection : Collection; + return (collection as Dictionary); + } + + /// + /// Called when the SyncType has been registered, but not yet initialized over the network. + /// + protected override void Registered() + { + base.Registered(); + foreach (KeyValuePair item in Collection) + _initialValues[item.Key] = item.Value; + } + + /// + /// Adds an operation and invokes callback locally. + /// Internal use. + /// May be used for custom SyncObjects. + /// + /// + /// + /// + [APIExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void AddOperation(SyncDictionaryOperation operation, TKey key, TValue value) + { + if (!base.IsRegistered) + return; + + /* asServer might be true if the client is setting the value + * through user code. Typically synctypes can only be set + * by the server, that's why it is assumed asServer via user code. + * However, when excluding owner for the synctype the client should + * have permission to update the value locally for use with + * prediction. */ + bool asServerInvoke = (!base.IsNetworkInitialized || base.NetworkBehaviour.IsServer); + + if (asServerInvoke) + { + _valuesChanged = true; + if (base.Dirty()) + { + ChangeData change = new ChangeData(operation, key, value); + _changed.Add(change); + } + } + + InvokeOnChange(operation, key, value, asServerInvoke); + } + + + /// + /// Called after OnStartXXXX has occurred. + /// + /// True if OnStartServer was called, false if OnStartClient. + public override void OnStartCallback(bool asServer) + { + base.OnStartCallback(asServer); + List collection = (asServer) ? _serverOnChanges : _clientOnChanges; + + if (OnChange != null) + { + foreach (CachedOnChange item in collection) + OnChange.Invoke(item.Operation, item.Key, item.Value, asServer); + } + + collection.Clear(); + } + + + /// + /// Writes all changed values. + /// Internal use. + /// May be used for custom SyncObjects. + /// + /// + ///True to set the next time data may sync. + [APIExclude] + public override void WriteDelta(PooledWriter writer, bool resetSyncTick = true) + { + base.WriteDelta(writer, resetSyncTick); + + //If sending all then clear changed and write full. + if (_sendAll) + { + _sendAll = false; + _changed.Clear(); + WriteFull(writer); + } + else + { + //False for not full write. + writer.WriteBoolean(false); + writer.WriteInt32(_changed.Count); + + for (int i = 0; i < _changed.Count; i++) + { + ChangeData change = _changed[i]; + writer.WriteByte((byte)change.Operation); + + //Clear does not need to write anymore data so it is not included in checks. + if (change.Operation == SyncDictionaryOperation.Add || + change.Operation == SyncDictionaryOperation.Set) + { + writer.Write(change.Key); + writer.Write(change.Value); + } + else if (change.Operation == SyncDictionaryOperation.Remove) + { + writer.Write(change.Key); + } + } + + _changed.Clear(); + } + } + + + /// + /// Writers all values if not initial values. + /// Internal use. + /// May be used for custom SyncObjects. + /// + /// + [APIExclude] + public override void WriteFull(PooledWriter writer) + { + if (!_valuesChanged) + return; + + base.WriteHeader(writer, false); + //True for full write. + writer.WriteBoolean(true); + writer.WriteInt32(Collection.Count); + foreach (KeyValuePair item in Collection) + { + writer.WriteByte((byte)SyncDictionaryOperation.Add); + writer.Write(item.Key); + writer.Write(item.Value); + } + } + + + /// + /// Reads and sets the current values for server or client. + /// + [APIExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void Read(PooledReader reader, bool asServer) + { + /* When !asServer don't make changes if server is running. + * This is because changes would have already been made on + * the server side and doing so again would result in duplicates + * and potentially overwrite data not yet sent. */ + bool asClientAndHost = (!asServer && base.NetworkBehaviour.IsServer); + IDictionary collection = (asClientAndHost) ? ClientHostCollection : Collection; + + //Clear collection since it's a full write. + bool fullWrite = reader.ReadBoolean(); + if (fullWrite) + collection.Clear(); + + int changes = reader.ReadInt32(); + for (int i = 0; i < changes; i++) + { + SyncDictionaryOperation operation = (SyncDictionaryOperation)reader.ReadByte(); + TKey key = default; + TValue value = default; + + /* Add, Set. + * Use the Set code for add and set, + * especially so collection doesn't throw + * if entry has already been added. */ + if (operation == SyncDictionaryOperation.Add || operation == SyncDictionaryOperation.Set) + { + key = reader.Read(); + value = reader.Read(); + collection[key] = value; + } + //Clear. + else if (operation == SyncDictionaryOperation.Clear) + { + collection.Clear(); + } + //Remove. + else if (operation == SyncDictionaryOperation.Remove) + { + key = reader.Read(); + collection.Remove(key); + } + + InvokeOnChange(operation, key, value, false); + } + + //If changes were made invoke complete after all have been read. + if (changes > 0) + InvokeOnChange(SyncDictionaryOperation.Complete, default, default, false); + } + + + /// + /// Invokes OnChanged callback. + /// + private void InvokeOnChange(SyncDictionaryOperation operation, TKey key, TValue value, bool asServer) + { + if (asServer) + { + if (base.NetworkBehaviour.OnStartServerCalled) + OnChange?.Invoke(operation, key, value, asServer); + else + _serverOnChanges.Add(new CachedOnChange(operation, key, value)); + } + else + { + if (base.NetworkBehaviour.OnStartClientCalled) + OnChange?.Invoke(operation, key, value, asServer); + else + _clientOnChanges.Add(new CachedOnChange(operation, key, value)); + } + } + + + /// + /// Resets to initialized values. + /// + [APIExclude] + public override void Reset() + { + base.Reset(); + _sendAll = false; + _changed.Clear(); + Collection.Clear(); + ClientHostCollection.Clear(); + _valuesChanged = false; + + foreach (KeyValuePair item in _initialValues) + { + Collection[item.Key] = item.Value; + ClientHostCollection[item.Key] = item.Value; + } + } + + + /// + /// Adds item. + /// + /// Item to add. + public void Add(KeyValuePair item) + { + Add(item.Key, item.Value); + } + /// + /// Adds key and value. + /// + /// Key to add. + /// Value for key. + public void Add(TKey key, TValue value) + { + Add(key, value, true); + } + private void Add(TKey key, TValue value, bool asServer) + { + if (!base.CanNetworkSetValues(true)) + return; + + Collection.Add(key, value); + if (asServer) + AddOperation(SyncDictionaryOperation.Add, key, value); + } + + /// + /// Clears all values. + /// + public void Clear() + { + Clear(true); + } + private void Clear(bool asServer) + { + if (!base.CanNetworkSetValues(true)) + return; + + Collection.Clear(); + if (asServer) + AddOperation(SyncDictionaryOperation.Clear, default, default); + } + + + /// + /// Returns if key exist. + /// + /// Key to use. + /// True if found. + public bool ContainsKey(TKey key) + { + return Collection.ContainsKey(key); + } + /// + /// Returns if item exist. + /// + /// Item to use. + /// True if found. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(KeyValuePair item) + { + return TryGetValue(item.Key, out TValue value) && EqualityComparer.Default.Equals(value, item.Value); + } + + /// + /// Copies collection to an array. + /// + /// Array to copy to. + /// Offset of array data is copied to. + public void CopyTo([NotNull] KeyValuePair[] array, int offset) + { + if (offset <= -1 || offset >= array.Length) + { + base.NetworkManager.LogError($"Index is out of range."); + return; + } + + int remaining = array.Length - offset; + if (remaining < Count) + { + base.NetworkManager.LogError($"Array is not large enough to copy data. Array is of length {array.Length}, index is {offset}, and number of values to be copied is {Count.ToString()}."); + return; + } + + int i = offset; + foreach (KeyValuePair item in Collection) + { + array[i] = item; + i++; + } + } + + + /// + /// Removes a key. + /// + /// Key to remove. + /// True if removed. + public bool Remove(TKey key) + { + if (!base.CanNetworkSetValues(true)) + return false; + + if (Collection.Remove(key)) + { + AddOperation(SyncDictionaryOperation.Remove, key, default); + return true; + } + + return false; + } + + + /// + /// Removes an item. + /// + /// Item to remove. + /// True if removed. + public bool Remove(KeyValuePair item) + { + return Remove(item.Key); + } + + /// + /// Tries to get value from key. + /// + /// Key to use. + /// Variable to output to. + /// True if able to output value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGetValue(TKey key, out TValue value) + { + return Collection.TryGetValueIL2CPP(key, out value); + } + + /// + /// Gets or sets value for a key. + /// + /// Key to use. + /// Value when using as Get. + public TValue this[TKey key] + { + get => Collection[key]; + set + { + if (!base.CanNetworkSetValues(true)) + return; + + Collection[key] = value; + AddOperation(SyncDictionaryOperation.Set, key, value); + } + } + + /// + /// Dirties the entire collection forcing a full send. + /// + public void DirtyAll() + { + if (!base.IsRegistered) + return; + if (!base.CanNetworkSetValues(true)) + return; + + if (base.Dirty()) + _sendAll = true; + } + + /// + /// Dirties an entry by key. + /// + /// Key to dirty. + public void Dirty(TKey key) + { + if (!base.IsRegistered) + return; + if (!base.CanNetworkSetValues(true)) + return; + + if (Collection.TryGetValueIL2CPP(key, out TValue value)) + AddOperation(SyncDictionaryOperation.Set, key, value); + } + + /// + /// Dirties an entry by value. + /// This operation can be very expensive, will cause allocations, and may fail if your value cannot be compared. + /// + /// Value to dirty. + /// True if value was found and marked dirty. + public bool Dirty(TValue value, EqualityComparer comparer = null) + { + if (!base.IsRegistered) + return false; + if (!base.CanNetworkSetValues(true)) + return false; + + if (comparer == null) + comparer = EqualityComparer.Default; + + foreach (KeyValuePair item in Collection) + { + if (comparer.Equals(item.Value, value)) + { + AddOperation(SyncDictionaryOperation.Set, item.Key, value); + return true; + } + } + + //Not found. + return false; + } + + /// + /// Gets the IEnumerator for the collection. + /// + /// + public IEnumerator> GetEnumerator() => Collection.GetEnumerator(); + /// + /// Gets the IEnumerator for the collection. + /// + /// + IEnumerator IEnumerable.GetEnumerator() => Collection.GetEnumerator(); + + } + + [APIExclude] + public class SyncDictionary : SyncIDictionary + { + [APIExclude] + public SyncDictionary() : base(new Dictionary()) { } + [APIExclude] + public SyncDictionary(IEqualityComparer eq) : base(new Dictionary(eq)) { } + [APIExclude] + public new Dictionary.ValueCollection Values => ((Dictionary)Collection).Values; + [APIExclude] + public new Dictionary.KeyCollection Keys => ((Dictionary)Collection).Keys; + [APIExclude] + public new Dictionary.Enumerator GetEnumerator() => ((Dictionary)Collection).GetEnumerator(); + + } +} diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs.meta new file mode 100644 index 0000000..10242a2 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 54751f912587a854cb61ff80a82087bf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionaryOperation.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionaryOperation.cs new file mode 100644 index 0000000..7f9913f --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionaryOperation.cs @@ -0,0 +1,31 @@ + +using FishNet.Documenting; + +namespace FishNet.Object.Synchronizing +{ + [APIExclude] + public enum SyncDictionaryOperation : byte + { + /// + /// A key and value have been added to the collection. + /// + Add, + /// + /// Collection has been cleared. + /// + Clear, + /// + /// A key was removed from the collection. + /// + Remove, + /// + /// A value has been set for a key in the collection. + /// + Set, + /// + /// All operations for the tick have been processed. + /// + Complete + } + +} diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionaryOperation.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionaryOperation.cs.meta new file mode 100644 index 0000000..2b2b24c --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionaryOperation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d5d6ed9db47a8224fa9ed4d2ff54586f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashSetOperation.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashSetOperation.cs new file mode 100644 index 0000000..01017b4 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashSetOperation.cs @@ -0,0 +1,31 @@ + +using FishNet.Documenting; + +namespace FishNet.Object.Synchronizing +{ + [APIExclude] + public enum SyncHashSetOperation : byte + { + /// + /// An item is added to the collection. + /// + Add, + /// + /// An item is removed from the collection. + /// + Remove, + /// + /// Collection is cleared. + /// + Clear, + /// + /// All operations for the tick have been processed. This only occurs on clients as the server is unable to be aware of when the user is done modifying the list. + /// + Complete, + /// + /// An item has been updated within the collection. This is generally used when modifying data within a container. + /// + Update, + } + +} diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashSetOperation.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashSetOperation.cs.meta new file mode 100644 index 0000000..2d909a8 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashSetOperation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 914089f5707003340a68fd6cd718e4c4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashset.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashset.cs new file mode 100644 index 0000000..e7e83ec --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashset.cs @@ -0,0 +1,616 @@ +using FishNet.Documenting; +using FishNet.Managing.Logging; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using FishNet.Serializing.Helping; +using FishNet.Utility.Performance; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Object.Synchronizing +{ + + public class SyncHashSet : SyncBase, ISet + { + #region Types. + /// + /// Information needed to invoke a callback. + /// + private struct CachedOnChange + { + internal readonly SyncHashSetOperation Operation; + internal readonly T Item; + + public CachedOnChange(SyncHashSetOperation operation, T item) + { + Operation = operation; + Item = item; + } + } + + /// + /// Information about how the collection has changed. + /// + private struct ChangeData + { + internal readonly SyncHashSetOperation Operation; + internal readonly T Item; + + public ChangeData(SyncHashSetOperation operation, T item) + { + Operation = operation; + + Item = item; + } + } + #endregion + + #region Public. + /// + /// Implementation from List. Not used. + /// + [APIExclude] + public bool IsReadOnly => false; + /// + /// Delegate signature for when SyncList changes. + /// + /// Type of change. + /// Item which was modified. + /// True if callback is occuring on the server. + [APIExclude] + public delegate void SyncHashSetChanged(SyncHashSetOperation op, T item, bool asServer); + /// + /// Called when the SyncList changes. + /// + public event SyncHashSetChanged OnChange; + /// + /// Collection of objects. + /// + public readonly ISet Collection; + /// + /// Copy of objects on client portion when acting as a host. + /// + public readonly ISet ClientHostCollection = new HashSet(); + /// + /// Number of objects in the collection. + /// + public int Count => Collection.Count; + #endregion + + #region Private. + /// + /// ListCache for comparing. + /// + private ListCache _listCache; + /// + /// Values upon initialization. + /// + private ISet _initialValues = new HashSet(); + /// + /// Comparer to see if entries change when calling public methods. + /// + private readonly IEqualityComparer _comparer; + /// + /// Changed data which will be sent next tick. + /// + private readonly List _changed = new List(); + /// + /// Server OnChange events waiting for start callbacks. + /// + private readonly List _serverOnChanges = new List(); + /// + /// Client OnChange events waiting for start callbacks. + /// + private readonly List _clientOnChanges = new List(); + /// + /// True if values have changed since initialization. + /// The only reasonable way to reset this during a Reset call is by duplicating the original list and setting all values to it on reset. + /// + private bool _valuesChanged; + /// + /// True to send all values in the next WriteDelta. + /// + private bool _sendAll; + #endregion + + [APIExclude] + public SyncHashSet() : this(new HashSet(), EqualityComparer.Default) { } + [APIExclude] + public SyncHashSet(IEqualityComparer comparer) : this(new HashSet(), (comparer == null) ? EqualityComparer.Default : comparer) { } + [APIExclude] + public SyncHashSet(ISet collection, IEqualityComparer comparer = null) + { + this._comparer = (comparer == null) ? EqualityComparer.Default : comparer; + this.Collection = collection; + //Add each in collection to clienthostcollection. + foreach (T item in collection) + ClientHostCollection.Add(item); + } + + /// + /// Called when the SyncType has been registered, but not yet initialized over the network. + /// + protected override void Registered() + { + base.Registered(); + foreach (T item in Collection) + _initialValues.Add(item); + } + + /// + /// Gets the collection being used within this SyncList. + /// + /// + public HashSet GetCollection(bool asServer) + { + bool asClientAndHost = (!asServer && base.NetworkManager.IsServer); + ISet collection = (asClientAndHost) ? ClientHostCollection : Collection; + return (collection as HashSet); + } + + /// + /// Adds an operation and invokes locally. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void AddOperation(SyncHashSetOperation operation, T item) + { + if (!base.IsRegistered) + return; + + bool asServerInvoke = (!base.IsNetworkInitialized || base.NetworkBehaviour.IsServer); + + if (asServerInvoke) + { + _valuesChanged = true; + if (base.Dirty()) + { + ChangeData change = new ChangeData(operation, item); + _changed.Add(change); + } + } + + InvokeOnChange(operation, item, asServerInvoke); + } + + /// + /// Called after OnStartXXXX has occurred. + /// + /// True if OnStartServer was called, false if OnStartClient. + public override void OnStartCallback(bool asServer) + { + base.OnStartCallback(asServer); + List collection = (asServer) ? _serverOnChanges : _clientOnChanges; + if (OnChange != null) + { + foreach (CachedOnChange item in collection) + OnChange.Invoke(item.Operation, item.Item, asServer); + } + + collection.Clear(); + } + + /// + /// Writes all changed values. + /// + /// + ///True to set the next time data may sync. + public override void WriteDelta(PooledWriter writer, bool resetSyncTick = true) + { + //If sending all then clear changed and write full. + if (_sendAll) + { + _sendAll = false; + _changed.Clear(); + WriteFull(writer); + } + else + { + base.WriteDelta(writer, resetSyncTick); + //False for not full write. + writer.WriteBoolean(false); + writer.WriteInt32(_changed.Count); + + for (int i = 0; i < _changed.Count; i++) + { + ChangeData change = _changed[i]; + writer.WriteByte((byte)change.Operation); + + //Clear does not need to write anymore data so it is not included in checks. + if (change.Operation == SyncHashSetOperation.Add || change.Operation == SyncHashSetOperation.Remove || change.Operation == SyncHashSetOperation.Update) + { + writer.Write(change.Item); + } + } + + _changed.Clear(); + } + } + + /// + /// Writes all values if not initial values. + /// + /// + public override void WriteFull(PooledWriter writer) + { + if (!_valuesChanged) + return; + + base.WriteHeader(writer, false); + //True for full write. + writer.WriteBoolean(true); + int count = Collection.Count; + writer.WriteInt32(count); + foreach (T item in Collection) + { + writer.WriteByte((byte)SyncHashSetOperation.Add); + writer.Write(item); + } + } + + /// + /// Reads and sets the current values for server or client. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [APIExclude] + public override void Read(PooledReader reader, bool asServer) + { + /* When !asServer don't make changes if server is running. + * This is because changes would have already been made on + * the server side and doing so again would result in duplicates + * and potentially overwrite data not yet sent. */ + bool asClientAndHost = (!asServer && base.NetworkManager.IsServer); + ISet collection = (asClientAndHost) ? ClientHostCollection : Collection; + + //Clear collection since it's a full write. + bool fullWrite = reader.ReadBoolean(); + if (fullWrite) + collection.Clear(); + + int changes = reader.ReadInt32(); + for (int i = 0; i < changes; i++) + { + SyncHashSetOperation operation = (SyncHashSetOperation)reader.ReadByte(); + T next = default; + + //Add. + if (operation == SyncHashSetOperation.Add) + { + next = reader.Read(); + collection.Add(next); + } + //Clear. + else if (operation == SyncHashSetOperation.Clear) + { + collection.Clear(); + } + //Remove. + else if (operation == SyncHashSetOperation.Remove) + { + next = reader.Read(); + collection.Remove(next); + } + //Updated. + else if (operation == SyncHashSetOperation.Update) + { + next = reader.Read(); + collection.Remove(next); + collection.Add(next); + } + + InvokeOnChange(operation, next, false); + } + + //If changes were made invoke complete after all have been read. + if (changes > 0) + InvokeOnChange(SyncHashSetOperation.Complete, default, false); + } + + /// + /// Invokes OnChanged callback. + /// + private void InvokeOnChange(SyncHashSetOperation operation, T item, bool asServer) + { + if (asServer) + { + if (base.NetworkBehaviour.OnStartServerCalled) + OnChange?.Invoke(operation, item, asServer); + else + _serverOnChanges.Add(new CachedOnChange(operation, item)); + } + else + { + if (base.NetworkBehaviour.OnStartClientCalled) + OnChange?.Invoke(operation, item, asServer); + else + _clientOnChanges.Add(new CachedOnChange(operation, item)); + } + } + + /// + /// Resets to initialized values. + /// + public override void Reset() + { + base.Reset(); + _sendAll = false; + _changed.Clear(); + Collection.Clear(); + ClientHostCollection.Clear(); + + foreach (T item in _initialValues) + { + Collection.Add(item); + ClientHostCollection.Add(item); + } + } + + /// + /// Adds value. + /// + /// + public bool Add(T item) + { + return Add(item, true); + } + private bool Add(T item, bool asServer) + { + if (!base.CanNetworkSetValues(true)) + return false; + + bool result = Collection.Add(item); + //Only process if remove was successful. + if (result && asServer) + { + if (base.NetworkManager == null) + ClientHostCollection.Add(item); + AddOperation(SyncHashSetOperation.Add, item); + } + + return result; + } + /// + /// Adds a range of values. + /// + /// + public void AddRange(IEnumerable range) + { + foreach (T entry in range) + Add(entry, true); + } + + /// + /// Clears all values. + /// + public void Clear() + { + Clear(true); + } + private void Clear(bool asServer) + { + if (!base.CanNetworkSetValues(true)) + return; + + Collection.Clear(); + if (asServer) + { + if (base.NetworkManager == null) + ClientHostCollection.Clear(); + AddOperation(SyncHashSetOperation.Clear, default); + } + } + + /// + /// Returns if value exist. + /// + /// + /// + public bool Contains(T item) + { + return Collection.Contains(item); + } + + /// + /// Removes a value. + /// + /// + /// + public bool Remove(T item) + { + return Remove(item, true); + } + private bool Remove(T item, bool asServer) + { + if (!base.CanNetworkSetValues(true)) + return false; + + bool result = Collection.Remove(item); + //Only process if remove was successful. + if (result && asServer) + { + if (base.NetworkManager == null) + ClientHostCollection.Remove(item); + AddOperation(SyncHashSetOperation.Remove, item); + } + + return result; + } + + /// + /// Dirties the entire collection forcing a full send. + /// + public void DirtyAll() + { + if (!base.IsRegistered) + return; + if (!base.CanNetworkSetValues(true)) + return; + + if (base.Dirty()) + _sendAll = true; + } + + /// + /// Looks up obj in Collection and if found marks it's index as dirty. + /// This operation can be very expensive, will cause allocations, and may fail if your value cannot be compared. + /// + /// Object to lookup. + public void Dirty(T obj) + { + if (!base.IsRegistered) + return; + if (!base.CanNetworkSetValues(true)) + return; + + foreach (T item in Collection) + { + if (item.Equals(obj)) + { + AddOperation(SyncHashSetOperation.Update, obj); + return; + } + } + + //Not found. + base.NetworkManager.LogError($"Could not find object within SyncHashSet, dirty will not be set."); + } + + /// + /// Returns Enumerator for collection. + /// + /// + public IEnumerator GetEnumerator() => Collection.GetEnumerator(); + [APIExclude] + IEnumerator IEnumerable.GetEnumerator() => Collection.GetEnumerator(); + [APIExclude] + IEnumerator IEnumerable.GetEnumerator() => Collection.GetEnumerator(); + + public void ExceptWith(IEnumerable other) + { + //Again, removing from self is a clear. + if (other == Collection) + { + Clear(); + } + else + { + foreach (T item in other) + Remove(item); + } + } + + public void IntersectWith(IEnumerable other) + { + ISet set; + if (other is ISet setA) + set = setA; + else + set = new HashSet(other); + + IntersectWith(set); + } + + private void IntersectWith(ISet other) + { + Intersect(Collection); + if (base.NetworkManager == null) + Intersect(ClientHostCollection); + + void Intersect(ISet collection) + { + if (_listCache == null) + _listCache = new ListCache(); + else + _listCache.Reset(); + + _listCache.AddValues(collection); + + int count = _listCache.Written; + for (int i = 0; i < count; i++) + { + T entry = _listCache.Collection[i]; + if (!other.Contains(entry)) + Remove(entry); + } + } + + } + + public bool IsProperSubsetOf(IEnumerable other) + { + return Collection.IsProperSubsetOf(other); + } + + public bool IsProperSupersetOf(IEnumerable other) + { + return Collection.IsProperSupersetOf(other); + } + + public bool IsSubsetOf(IEnumerable other) + { + return Collection.IsSubsetOf(other); + } + + public bool IsSupersetOf(IEnumerable other) + { + return Collection.IsSupersetOf(other); + } + + public bool Overlaps(IEnumerable other) + { + bool result = Collection.Overlaps(other); + return result; + } + + public bool SetEquals(IEnumerable other) + { + return Collection.SetEquals(other); + } + + public void SymmetricExceptWith(IEnumerable other) + { + //If calling except on self then that is the same as a clear. + if (other == Collection) + { + Clear(); + } + else + { + foreach (T item in other) + Remove(item); + } + } + + public void UnionWith(IEnumerable other) + { + if (other == Collection) + return; + + foreach (T item in other) + Add(item); + } + + /// + /// Adds an item. + /// + /// + void ICollection.Add(T item) + { + Add(item, true); + } + + /// + /// Copies values to an array. + /// + /// + /// + public void CopyTo(T[] array, int index) + { + Collection.CopyTo(array, index); + if (base.NetworkManager == null) + ClientHostCollection.CopyTo(array, index); + } + } +} diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashset.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashset.cs.meta new file mode 100644 index 0000000..47a2d07 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashset.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 624322b9d999d4b43a560134460955c6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncList.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncList.cs new file mode 100644 index 0000000..a025196 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncList.cs @@ -0,0 +1,748 @@ +using FishNet.Documenting; +using FishNet.Managing.Logging; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Object.Synchronizing +{ + + public class SyncList : SyncBase, IList, IReadOnlyList + { + #region Types. + /// + /// Information needed to invoke a callback. + /// + private struct CachedOnChange + { + internal readonly SyncListOperation Operation; + internal readonly int Index; + internal readonly T Previous; + internal readonly T Next; + + public CachedOnChange(SyncListOperation operation, int index, T previous, T next) + { + Operation = operation; + Index = index; + Previous = previous; + Next = next; + } + } + + /// + /// Information about how the collection has changed. + /// + private struct ChangeData + { + internal readonly SyncListOperation Operation; + internal readonly int Index; + internal readonly T Item; + + public ChangeData(SyncListOperation operation, int index, T item) + { + Operation = operation; + Index = index; + Item = item; + } + } + + /// + /// Custom enumerator to prevent garbage collection. + /// + [APIExclude] + public struct Enumerator : IEnumerator + { + public T Current { get; private set; } + private readonly SyncList _list; + private int _index; + + public Enumerator(SyncList list) + { + this._list = list; + _index = -1; + Current = default; + } + + public bool MoveNext() + { + _index++; + if (_index >= _list.Count) + return false; + Current = _list[_index]; + return true; + } + + public void Reset() => _index = -1; + object IEnumerator.Current => Current; + public void Dispose() { } + } + #endregion + + #region Public. + /// + /// Implementation from List. Not used. + /// + [APIExclude] + public bool IsReadOnly => false; + /// + /// Delegate signature for when SyncList changes. + /// + /// + /// + /// + /// + [APIExclude] + public delegate void SyncListChanged(SyncListOperation op, int index, T oldItem, T newItem, bool asServer); + /// + /// Called when the SyncList changes. + /// + public event SyncListChanged OnChange; + /// + /// Collection of objects. + /// + public readonly IList Collection; + /// + /// Copy of objects on client portion when acting as a host. + /// + public readonly IList ClientHostCollection = new List(); + /// + /// Number of objects in the collection. + /// + public int Count => Collection.Count; + #endregion + + #region Private. + /// + /// Values upon initialization. + /// + private IList _initialValues = new List(); + /// + /// Comparer to see if entries change when calling public methods. + /// + private readonly IEqualityComparer _comparer; + /// + /// Changed data which will be sent next tick. + /// + private readonly List _changed = new List(); + /// + /// Server OnChange events waiting for start callbacks. + /// + private readonly List _serverOnChanges = new List(); + /// + /// Client OnChange events waiting for start callbacks. + /// + private readonly List _clientOnChanges = new List(); + /// + /// True if values have changed since initialization. + /// The only reasonable way to reset this during a Reset call is by duplicating the original list and setting all values to it on reset. + /// + private bool _valuesChanged; + /// + /// True to send all values in the next WriteDelta. + /// + private bool _sendAll; + #endregion + + [APIExclude] + public SyncList() : this(new List(), EqualityComparer.Default) { } + [APIExclude] + public SyncList(IEqualityComparer comparer) : this(new List(), (comparer == null) ? EqualityComparer.Default : comparer) { } + [APIExclude] + public SyncList(IList collection, IEqualityComparer comparer = null) + { + this._comparer = (comparer == null) ? EqualityComparer.Default : comparer; + this.Collection = collection; + //Add each in collection to clienthostcollection. + foreach (T item in collection) + this.ClientHostCollection.Add(item); + } + + /// + /// Called when the SyncType has been registered, but not yet initialized over the network. + /// + protected override void Registered() + { + base.Registered(); + foreach (T item in Collection) + _initialValues.Add(item); + } + + /// + /// Gets the collection being used within this SyncList. + /// + /// True if returning the server value, false if client value. The values will only differ when running as host. While asServer is true the most current values on server will be returned, and while false the latest values received by client will be returned. + /// + public List GetCollection(bool asServer) + { + bool asClientAndHost = (!asServer && base.NetworkManager.IsServer); + IList collection = (asClientAndHost) ? ClientHostCollection : Collection; + return (collection as List); + } + + /// + /// Adds an operation and invokes locally. + /// + /// + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void AddOperation(SyncListOperation operation, int index, T prev, T next) + { + if (!base.IsRegistered) + return; + + /* asServer might be true if the client is setting the value + * through user code. Typically synctypes can only be set + * by the server, that's why it is assumed asServer via user code. + * However, when excluding owner for the synctype the client should + * have permission to update the value locally for use with + * prediction. */ + bool asServerInvoke = (!base.IsNetworkInitialized || base.NetworkBehaviour.IsServer); + + /* Only the adds asServer may set + * this synctype as dirty and add + * to pending changes. However, the event may still + * invoke for clientside. */ + if (asServerInvoke) + { + /* Set as changed even if cannot dirty. + * Dirty is only set when there are observers, + * but even if there are not observers + * values must be marked as changed so when + * there are observers, new values are sent. */ + _valuesChanged = true; + + /* If unable to dirty then do not add to changed. + * A dirty may fail if the server is not started + * or if there's no observers. Changed doesn't need + * to be populated in this situations because clients + * will get the full collection on spawn. If we + * were to also add to changed clients would get the full + * collection as well the changed, which would double results. */ + if (base.Dirty()) + { + ChangeData change = new ChangeData(operation, index, next); + _changed.Add(change); + } + } + + InvokeOnChange(operation, index, prev, next, asServerInvoke); + } + + /// + /// Called after OnStartXXXX has occurred. + /// + /// True if OnStartServer was called, false if OnStartClient. + public override void OnStartCallback(bool asServer) + { + base.OnStartCallback(asServer); + List collection = (asServer) ? _serverOnChanges : _clientOnChanges; + + if (OnChange != null) + { + foreach (CachedOnChange item in collection) + OnChange.Invoke(item.Operation, item.Index, item.Previous, item.Next, asServer); + } + + collection.Clear(); + } + + /// + /// Writes all changed values. + /// + /// + ///True to set the next time data may sync. + public override void WriteDelta(PooledWriter writer, bool resetSyncTick = true) + { + //If sending all then clear changed and write full. + if (_sendAll) + { + _sendAll = false; + _changed.Clear(); + WriteFull(writer); + } + else + { + base.WriteDelta(writer, resetSyncTick); + //False for not full write. + writer.WriteBoolean(false); + writer.WriteInt32(_changed.Count); + + for (int i = 0; i < _changed.Count; i++) + { + ChangeData change = _changed[i]; + writer.WriteByte((byte)change.Operation); + + //Clear does not need to write anymore data so it is not included in checks. + if (change.Operation == SyncListOperation.Add) + { + writer.Write(change.Item); + } + else if (change.Operation == SyncListOperation.RemoveAt) + { + writer.WriteInt32(change.Index); + } + else if (change.Operation == SyncListOperation.Insert || change.Operation == SyncListOperation.Set) + { + writer.WriteInt32(change.Index); + writer.Write(change.Item); + } + } + + _changed.Clear(); + } + } + + /// + /// Writes all values if not initial values. + /// + /// + public override void WriteFull(PooledWriter writer) + { + if (!_valuesChanged) + return; + + base.WriteHeader(writer, false); + //True for full write. + writer.WriteBoolean(true); + writer.WriteInt32(Collection.Count); + for (int i = 0; i < Collection.Count; i++) + { + writer.WriteByte((byte)SyncListOperation.Add); + writer.Write(Collection[i]); + } + } + + /// + /// Reads and sets the current values for server or client. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [APIExclude] + public override void Read(PooledReader reader, bool asServer) + { + /* When !asServer don't make changes if server is running. + * This is because changes would have already been made on + * the server side and doing so again would result in duplicates + * and potentially overwrite data not yet sent. */ + bool asClientAndHost = (!asServer && base.NetworkManager.IsServer); + IList collection = (asClientAndHost) ? ClientHostCollection : Collection; + + //Clear collection since it's a full write. + bool fullWrite = reader.ReadBoolean(); + if (fullWrite) + collection.Clear(); + + int changes = reader.ReadInt32(); + + for (int i = 0; i < changes; i++) + { + SyncListOperation operation = (SyncListOperation)reader.ReadByte(); + int index = -1; + T prev = default; + T next = default; + + //Add. + if (operation == SyncListOperation.Add) + { + next = reader.Read(); + index = collection.Count; + collection.Add(next); + } + //Clear. + else if (operation == SyncListOperation.Clear) + { + collection.Clear(); + } + //Insert. + else if (operation == SyncListOperation.Insert) + { + index = reader.ReadInt32(); + next = reader.Read(); + collection.Insert(index, next); + } + //RemoveAt. + else if (operation == SyncListOperation.RemoveAt) + { + index = reader.ReadInt32(); + prev = collection[index]; + collection.RemoveAt(index); + } + //Set + else if (operation == SyncListOperation.Set) + { + index = reader.ReadInt32(); + next = reader.Read(); + prev = collection[index]; + collection[index] = next; + } + + InvokeOnChange(operation, index, prev, next, false); + } + + //If changes were made invoke complete after all have been read. + if (changes > 0) + InvokeOnChange(SyncListOperation.Complete, -1, default, default, false); + } + + /// + /// Invokes OnChanged callback. + /// + private void InvokeOnChange(SyncListOperation operation, int index, T prev, T next, bool asServer) + { + if (asServer) + { + if (base.NetworkBehaviour.OnStartServerCalled) + OnChange?.Invoke(operation, index, prev, next, asServer); + else + _serverOnChanges.Add(new CachedOnChange(operation, index, prev, next)); + } + else + { + if (base.NetworkBehaviour.OnStartClientCalled) + OnChange?.Invoke(operation, index, prev, next, asServer); + else + _clientOnChanges.Add(new CachedOnChange(operation, index, prev, next)); + } + } + + /// + /// Resets to initialized values. + /// + public override void Reset() + { + base.Reset(); + _sendAll = false; + _changed.Clear(); + ClientHostCollection.Clear(); + Collection.Clear(); + + foreach (T item in _initialValues) + { + Collection.Add(item); + ClientHostCollection.Add(item); + } + } + + + /// + /// Adds value. + /// + /// + public void Add(T item) + { + Add(item, true); + } + private void Add(T item, bool asServer) + { + if (!base.CanNetworkSetValues(true)) + return; + + Collection.Add(item); + if (asServer) + { + if (base.NetworkManager == null) + ClientHostCollection.Add(item); + AddOperation(SyncListOperation.Add, Collection.Count - 1, default, item); + } + } + /// + /// Adds a range of values. + /// + /// + public void AddRange(IEnumerable range) + { + foreach (T entry in range) + Add(entry, true); + } + + /// + /// Clears all values. + /// + public void Clear() + { + Clear(true); + } + private void Clear(bool asServer) + { + if (!base.CanNetworkSetValues(true)) + return; + + Collection.Clear(); + if (asServer) + { + if (base.NetworkManager == null) + ClientHostCollection.Clear(); + AddOperation(SyncListOperation.Clear, -1, default, default); + } + } + + /// + /// Returns if value exist. + /// + /// + /// + public bool Contains(T item) + { + return (IndexOf(item) >= 0); + } + + /// + /// Copies values to an array. + /// + /// + /// + public void CopyTo(T[] array, int index) + { + Collection.CopyTo(array, index); + } + + /// + /// Gets the index of value. + /// + /// + /// + public int IndexOf(T item) + { + for (int i = 0; i < Collection.Count; ++i) + if (_comparer.Equals(item, Collection[i])) + return i; + return -1; + } + + /// + /// Finds index using match. + /// + /// + /// + public int FindIndex(Predicate match) + { + for (int i = 0; i < Collection.Count; ++i) + if (match(Collection[i])) + return i; + return -1; + } + + /// + /// Finds value using match. + /// + /// + /// + public T Find(Predicate match) + { + int i = FindIndex(match); + return (i != -1) ? Collection[i] : default; + } + + /// + /// Finds all values using match. + /// + /// + /// + public List FindAll(Predicate match) + { + List results = new List(); + for (int i = 0; i < Collection.Count; ++i) + if (match(Collection[i])) + results.Add(Collection[i]); + return results; + } + + /// + /// Inserts value at index. + /// + /// + /// + public void Insert(int index, T item) + { + Insert(index, item, true); + } + private void Insert(int index, T item, bool asServer) + { + if (!base.CanNetworkSetValues(true)) + return; + + Collection.Insert(index, item); + if (asServer) + { + if (base.NetworkManager == null) + ClientHostCollection.Insert(index, item); + AddOperation(SyncListOperation.Insert, index, default, item); + } + } + + /// + /// Inserts a range of values. + /// + /// + /// + public void InsertRange(int index, IEnumerable range) + { + foreach (T entry in range) + { + Insert(index, entry); + index++; + } + } + + /// + /// Removes a value. + /// + /// + /// + public bool Remove(T item) + { + int index = IndexOf(item); + bool result = index >= 0; + if (result) + RemoveAt(index); + + return result; + } + + /// + /// Removes value at index. + /// + /// + /// + public void RemoveAt(int index) + { + RemoveAt(index, true); + } + private void RemoveAt(int index, bool asServer) + { + if (!base.CanNetworkSetValues(true)) + return; + + T oldItem = Collection[index]; + Collection.RemoveAt(index); + if (asServer) + { + if (base.NetworkManager == null) + ClientHostCollection.RemoveAt(index); + AddOperation(SyncListOperation.RemoveAt, index, oldItem, default); + } + } + + /// + /// Removes all values within the collection. + /// + /// + /// + public int RemoveAll(Predicate match) + { + List toRemove = new List(); + for (int i = 0; i < Collection.Count; ++i) + if (match(Collection[i])) + toRemove.Add(Collection[i]); + + foreach (T entry in toRemove) + Remove(entry); + + return toRemove.Count; + } + + /// + /// Gets or sets value at an index. + /// + /// + /// + public T this[int i] + { + get => Collection[i]; + set => Set(i, value, true, true); + } + + /// + /// Dirties the entire collection forcing a full send. + /// This will not invoke the callback on server. + /// + public void DirtyAll() + { + if (!base.IsRegistered) + return; + + if (base.NetworkManager != null && !base.NetworkBehaviour.IsServer) + { + base.NetworkManager.LogWarning($"Cannot complete operation as server when server is not active."); + return; + } + + if (base.Dirty()) + _sendAll = true; + } + + /// + /// Looks up obj in Collection and if found marks it's index as dirty. + /// While using this operation previous value will be the same as next. + /// This operation can be very expensive, and may fail if your value cannot be compared. + /// + /// Object to lookup. + public void Dirty(T obj) + { + int index = Collection.IndexOf(obj); + if (index != -1) + Dirty(index); + else + base.NetworkManager.LogError($"Could not find object within SyncList, dirty will not be set."); + } + /// + /// Marks an index as dirty. + /// While using this operation previous value will be the same as next. + /// + /// + public void Dirty(int index) + { + if (!base.CanNetworkSetValues(true)) + return; + + bool asServer = true; + T value = Collection[index]; + if (asServer) + AddOperation(SyncListOperation.Set, index, value, value); + } + /// + /// Sets value at index. + /// + /// + /// + public void Set(int index, T value, bool force = true) + { + Set(index, value, true, force); + } + private void Set(int index, T value, bool asServer, bool force) + { + if (!base.CanNetworkSetValues(true)) + return; + + bool sameValue = (!force && !_comparer.Equals(Collection[index], value)); + if (!sameValue) + { + T prev = Collection[index]; + Collection[index] = value; + if (asServer) + { + if (base.NetworkManager == null) + ClientHostCollection[index] = value; + AddOperation(SyncListOperation.Set, index, prev, value); + } + } + } + + + /// + /// Returns Enumerator for collection. + /// + /// + public Enumerator GetEnumerator() => new Enumerator(this); + [APIExclude] + IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this); + [APIExclude] + IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this); + + } +} diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncList.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/SyncList.cs.meta new file mode 100644 index 0000000..ba053ad --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncList.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2f3a4c0d0a34e5142be66143d732c079 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncListOperation.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncListOperation.cs new file mode 100644 index 0000000..324ac62 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncListOperation.cs @@ -0,0 +1,35 @@ + +using FishNet.Documenting; + +namespace FishNet.Object.Synchronizing +{ + [APIExclude] + public enum SyncListOperation : byte + { + /// + /// An item is added to the collection. + /// + Add, + /// + /// An item is inserted into the collection. + /// + Insert, + /// + /// An item is set in the collection. + /// + Set, + /// + /// An item is removed from the collection. + /// + RemoveAt, + /// + /// Collection is cleared. + /// + Clear, + /// + /// All operations for the tick have been processed. This only occurs on clients as the server is unable to be aware of when the user is done modifying the list. + /// + Complete + } + +} diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncListOperation.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/SyncListOperation.cs.meta new file mode 100644 index 0000000..a87c886 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncListOperation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4fa53fc807605df4997f0b63a6570bcf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncVar.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncVar.cs new file mode 100644 index 0000000..bce03fa --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncVar.cs @@ -0,0 +1,290 @@ +using FishNet.Documenting; +using FishNet.Object.Helping; +using FishNet.Object.Synchronizing; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using FishNet.Serializing.Helping; +using FishNet.Transporting; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using UnityEngine; + +namespace FishNet.Object.Synchronizing +{ + [APIExclude] + [StructLayout(LayoutKind.Auto, CharSet = CharSet.Auto)] + public class SyncVar : SyncBase + { + #region Types. + /// + /// Information needed to invoke a callback. + /// + private struct CachedOnChange + { + internal readonly T Previous; + internal readonly T Next; + + public CachedOnChange(T previous, T next) + { + Previous = previous; + Next = next; + } + } + #endregion + + #region Public. + /// + /// Called when the SyncDictionary changes. + /// + public event Action OnChange; + #endregion + + #region Private. + /// + /// Server OnChange event waiting for start callbacks. + /// + private CachedOnChange? _serverOnChange; + /// + /// Client OnChange event waiting for start callbacks. + /// + private CachedOnChange? _clientOnChange; + /// + /// Value before the network is initialized on the containing object. + /// + private T _initialValue; + /// + /// Previous value on the client. + /// + private T _previousClientValue; + /// + /// Current value on the server, or client. + /// + private T _value; + #endregion + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public SyncVar(NetworkBehaviour nb, uint syncIndex, WritePermission writePermission, ReadPermission readPermission, float sendRate, Channel channel, T value) + { + SetInitialValues(value); + base.InitializeInstance(nb, syncIndex, writePermission, readPermission, sendRate, channel, false); + } + + /// + /// Called when the SyncType has been registered, but not yet initialized over the network. + /// + protected override void Registered() + { + base.Registered(); + _initialValue = _value; + } + + /// + /// Sets initial values to next. + /// + /// + private void SetInitialValues(T next) + { + _initialValue = next; + UpdateValues(next); + } + /// + /// Sets current and previous values. + /// + /// + private void UpdateValues(T next) + { + _previousClientValue = next; + _value = next; + } + /// + /// Sets current value and marks the SyncVar dirty when able to. Returns if able to set value. + /// + /// True if SetValue was called in response to user code. False if from automated code. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetValue(T nextValue, bool calledByUser) + { + /* If not registered then that means Awake + * has not completed on the owning class. This would be true + * when setting values within awake on the owning class. Registered + * is called at the end of awake, so it would be unset until awake completed. + * + * Registered however will be true when setting from another script, + * even if the owning class of this was just spawned. This is because + * the unity cycle will fire awake on the object soon as it's spawned, + * completing awake, and the user would set the value after. */ + if (!base.IsRegistered) + return; + + /* If not client or server then set skipChecks + * as true. When neither is true it's likely user is changing + * value before object is initialized. This is allowed + * but checks cannot be processed because they would otherwise + * stop setting the value. */ + bool isNetworkInitialized = base.IsNetworkInitialized; + + //Object is deinitializing. + if (isNetworkInitialized && CodegenHelper.NetworkObject_Deinitializing(this.NetworkBehaviour)) + return; + + //If being set by user code. + if (calledByUser) + { + if (!base.CanNetworkSetValues(true)) + return; + + /* We will only be this far if the network is not active yet, + * server is active, or client has setting permissions. + * We only need to set asServerInvoke to false if the network + * is initialized and the server is not active. */ + bool asServerInvoke = (!isNetworkInitialized || base.NetworkBehaviour.IsServer); + + /* If the network has not been network initialized then + * Value is expected to be set on server and client since + * it's being set before the object is initialized. */ + if (!isNetworkInitialized) + { + T prev = _value; + UpdateValues(nextValue); + //Still call invoke because change will be cached for when the network initializes. + InvokeOnChange(prev, _value, calledByUser); + } + else + { + if (Comparers.EqualityCompare(_value, nextValue)) + return; + + T prev = _value; + _value = nextValue; + InvokeOnChange(prev, _value, asServerInvoke); + } + + TryDirty(asServerInvoke); + } + //Not called by user. + else + { + /* Previously clients were not allowed to set values + * but this has been changed because clients may want + * to update values locally while occasionally + * letting the syncvar adjust their side. */ + T prev = _previousClientValue; + if (Comparers.EqualityCompare(prev, nextValue)) + return; + + /* If also server do not update value. + * Server side has say of the current value. */ + if (!base.NetworkManager.IsServer) + UpdateValues(nextValue); + else + _previousClientValue = nextValue; + + InvokeOnChange(prev, nextValue, calledByUser); + } + + + /* Tries to dirty so update + * is sent over network. This needs to be called + * anytime the data changes because there is no way + * to know if the user set the value on both server + * and client or just one side. */ + void TryDirty(bool asServer) + { + //Cannot dirty when network is not initialized. + if (!isNetworkInitialized) + return; + + if (asServer) + base.Dirty(); + } + } + + /// + /// Invokes OnChanged callback. + /// + private void InvokeOnChange(T prev, T next, bool asServer) + { + if (asServer) + { + if (base.NetworkBehaviour.OnStartServerCalled) + OnChange?.Invoke(prev, next, asServer); + else + _serverOnChange = new CachedOnChange(prev, next); + } + else + { + if (base.NetworkBehaviour.OnStartClientCalled) + OnChange?.Invoke(prev, next, asServer); + else + _clientOnChange = new CachedOnChange(prev, next); + } + } + + + /// + /// Called after OnStartXXXX has occurred. + /// + /// True if OnStartServer was called, false if OnStartClient. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void OnStartCallback(bool asServer) + { + base.OnStartCallback(asServer); + + if (OnChange != null) + { + CachedOnChange? change = (asServer) ? _serverOnChange : _clientOnChange; + if (change != null) + InvokeOnChange(change.Value.Previous, change.Value.Next, asServer); + } + + if (asServer) + _serverOnChange = null; + else + _clientOnChange = null; + } + + /// + /// Writes current value. + /// + /// True to set the next time data may sync. + public override void WriteDelta(PooledWriter writer, bool resetSyncTick = true) + { + base.WriteDelta(writer, resetSyncTick); + writer.Write(_value); + } + + /// + /// Writes current value if not initialized value. + /// m> + public override void WriteFull(PooledWriter obj0) + { + if (Comparers.EqualityCompare(_initialValue, _value)) + return; + /* SyncVars only hold latest value, so just + * write current delta. */ + WriteDelta(obj0, false); + } + + //Read isn't used by SyncVar, it's done within the NB. + //public override void Read(PooledReader reader) { } + + /// + /// Gets current value. + /// + /// + /// + public T GetValue(bool calledByUser) => (calledByUser) ? _value : _previousClientValue; + + /// + /// Resets to initialized values. + /// + public override void Reset() + { + base.Reset(); + _value = _initialValue; + _previousClientValue = _initialValue; + } + } +} + + diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncVar.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/SyncVar.cs.meta new file mode 100644 index 0000000..5ae840f --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncVar.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f319403eec508734a93d723617ab1136 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/WritePermissions.cs b/Assets/FishNet/Runtime/Object/Synchronizing/WritePermissions.cs new file mode 100644 index 0000000..fbd2d15 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/WritePermissions.cs @@ -0,0 +1,10 @@ +namespace FishNet.Object.Synchronizing +{ + /// + /// Which clients or server may write updates. + /// + public enum WritePermission + { + ServerOnly + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/WritePermissions.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/WritePermissions.cs.meta new file mode 100644 index 0000000..d1fb70b --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/WritePermissions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2696d0da2ff02e8499a8351a3021008f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/TransformProperties.cs b/Assets/FishNet/Runtime/Object/TransformProperties.cs new file mode 100644 index 0000000..bbe05ee --- /dev/null +++ b/Assets/FishNet/Runtime/Object/TransformProperties.cs @@ -0,0 +1,20 @@ +using UnityEngine; + +namespace FishNet.Object +{ + [System.Serializable] + public struct TransformProperties + { + public readonly Vector3 Position; + public readonly Quaternion Rotation; + public readonly Vector3 LocalScale; + + public TransformProperties(Vector3 position, Quaternion rotation, Vector3 localScale) + { + Position = position; + Rotation = rotation; + LocalScale = localScale; + } + } +} + diff --git a/Assets/FishNet/Runtime/Object/TransformProperties.cs.meta b/Assets/FishNet/Runtime/Object/TransformProperties.cs.meta new file mode 100644 index 0000000..ec7f7aa --- /dev/null +++ b/Assets/FishNet/Runtime/Object/TransformProperties.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8e4ce2bc25fe8364d8b443f5ac7591ae +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Observing.meta b/Assets/FishNet/Runtime/Observing.meta new file mode 100644 index 0000000..e4085bd --- /dev/null +++ b/Assets/FishNet/Runtime/Observing.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e0cd196f74e1a994ebbc7a4cbd36eaf4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Observing/Conditions.meta b/Assets/FishNet/Runtime/Observing/Conditions.meta new file mode 100644 index 0000000..4b1cbbd --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 96ad2addbde8a81458e695b6797ae56e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Observing/Conditions/DistanceCondition.cs b/Assets/FishNet/Runtime/Observing/Conditions/DistanceCondition.cs new file mode 100644 index 0000000..c1c5ee8 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/DistanceCondition.cs @@ -0,0 +1,143 @@ +using FishNet.Connection; +using FishNet.Object; +using FishNet.Observing; +using FishNet.Utility.Extension; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Component.Observing +{ + /// + /// When this observer condition is placed on an object, a client must be within the specified distance to view the object. + /// + [CreateAssetMenu(menuName = "FishNet/Observers/Distance Condition", fileName = "New Distance Condition")] + public class DistanceCondition : ObserverCondition + { + #region Serialized. + /// + /// + /// + [Tooltip("Maximum distance a client must be within this object to see it.")] + [SerializeField] + private float _maximumDistance = 100f; + /// + /// Maximum distance a client must be within this object to see it. + /// + public float MaximumDistance { get => _maximumDistance; set => _maximumDistance = value; } + /// + /// Additional percent of distance client must be until this object is hidden. For example, if distance was 100f and percent was 0.5f the client must be 150f units away before this object is hidden again. This can be useful for keeping objects from regularly appearing and disappearing. + /// + [Tooltip("Additional percent of distance client must be until this object is hidden. For example, if distance was 100f and percent was 0.5f the client must be 150f units away before this object is hidden again. This can be useful for keeping objects from regularly appearing and disappearing.")] + [Range(0f, 1f)] + [SerializeField] + private float _hideDistancePercent = 0.1f; + /// + /// + /// + [Tooltip("How often this condition may change for a connection. This prevents objects from appearing and disappearing rapidly. A value of 0f will cause the object the update quickly as possible while any other value will be used as a delay.")] + [Range(0f, 60f)] + [SerializeField] + private float _updateFrequency; + /// + /// How often this condition may change for a connection. This prevents objects from appearing and disappearing rapidly. A value of 0f will cause the object the update quickly as possible while any other value will be used as a delay. + /// + public float UpdateFrequency { get => _updateFrequency; set => _updateFrequency = value; } + #endregion + + #region Private. + /// + /// Tracks when connections may be updated for this object. + /// + private Dictionary _timedUpdates = new Dictionary(); + #endregion + + public void ConditionConstructor(float maximumDistance, float updateFrequency) + { + MaximumDistance = maximumDistance; + _updateFrequency = updateFrequency; + } + + /// + /// Returns if the object which this condition resides should be visible to connection. + /// + /// Connection which the condition is being checked for. + /// True if the connection currently has visibility of this object. + /// True if the condition was not processed. This can be used to skip processing for performance. While output as true this condition result assumes the previous ConditionMet value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool ConditionMet(NetworkConnection connection, bool currentlyAdded, out bool notProcessed) + { + if (_updateFrequency > 0f) + { + float nextAllowedUpdate; + float currentTime = Time.time; + if (!_timedUpdates.TryGetValueIL2CPP(connection, out nextAllowedUpdate)) + { + _timedUpdates[connection] = (currentTime + _updateFrequency); + } + else + { + //Not enough time to process again. + if (currentTime < nextAllowedUpdate) + { + notProcessed = true; + //The return does not really matter since notProcessed is returned. + return false; + } + //Can process again. + else + { + _timedUpdates[connection] = (currentTime + _updateFrequency); + } + } + } + //If here then checks are being processed. + notProcessed = false; + + float sqrMaximumDistance; + /* If already visible then use additional + * distance to determine when to hide. */ + if (currentlyAdded) + { + float maxModified = (MaximumDistance * (1f + _hideDistancePercent)); + sqrMaximumDistance = (maxModified * maxModified); + } + //Not visible, use normal distance. + else + { + sqrMaximumDistance = (MaximumDistance * MaximumDistance); + } + + Vector3 thisPosition = NetworkObject.transform.position; + foreach (NetworkObject nob in connection.Objects) + { + //If within distance. + if (Vector3.SqrMagnitude(nob.transform.position - thisPosition) <= sqrMaximumDistance) + return true; + } + + /* If here no client objects are within distance. */ + return false; + } + + /// + /// True if the condition requires regular updates. + /// + /// + public override bool Timed() + { + return true; + } + + /// + /// Clones referenced ObserverCondition. This must be populated with your conditions settings. + /// + /// + public override ObserverCondition Clone() + { + DistanceCondition copy = ScriptableObject.CreateInstance(); + copy.ConditionConstructor(MaximumDistance, _updateFrequency); + return copy; + } + } +} diff --git a/Assets/FishNet/Runtime/Observing/Conditions/DistanceCondition.cs.meta b/Assets/FishNet/Runtime/Observing/Conditions/DistanceCondition.cs.meta new file mode 100644 index 0000000..0ee3ca9 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/DistanceCondition.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7c3e28fa2e37d1d41b4f63c8a0cc2553 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Observing/Conditions/HostOnlyCondition.cs b/Assets/FishNet/Runtime/Observing/Conditions/HostOnlyCondition.cs new file mode 100644 index 0000000..3de1c14 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/HostOnlyCondition.cs @@ -0,0 +1,30 @@ +using FishNet.Connection; +using FishNet.Observing; +using UnityEngine; + +namespace FishNet.Component.Observing +{ + [CreateAssetMenu(menuName = "FishNet/Observers/Host Only Condition", fileName = "New Host Only Condition")] + public class HostOnlyCondition : ObserverCondition + { + public override bool ConditionMet(NetworkConnection connection, bool currentlyAdded, out bool notProcessed) + { + notProcessed = false; + /* Only return true if connection is the local client. + * This check only runs on the server, so if local client + * is true then they must also be the server (clientHost). */ + return (base.NetworkObject.ClientManager.Connection == connection); + } + + public override bool Timed() + { + return false; + } + + public override ObserverCondition Clone() + { + HostOnlyCondition copy = ScriptableObject.CreateInstance(); + return copy; + } + } +} diff --git a/Assets/FishNet/Runtime/Observing/Conditions/HostOnlyCondition.cs.meta b/Assets/FishNet/Runtime/Observing/Conditions/HostOnlyCondition.cs.meta new file mode 100644 index 0000000..2af0f62 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/HostOnlyCondition.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aa62c0af0c0a4da46b03309dcd3858c3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Observing/Conditions/MatchCondition.cs b/Assets/FishNet/Runtime/Observing/Conditions/MatchCondition.cs new file mode 100644 index 0000000..c4b8f60 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/MatchCondition.cs @@ -0,0 +1,585 @@ +using FishNet.Connection; +using FishNet.Managing; +using FishNet.Managing.Logging; +using FishNet.Managing.Server; +using FishNet.Object; +using FishNet.Observing; +using FishNet.Utility.Extension; +using FishNet.Utility.Performance; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Component.Observing +{ + /// + /// When this observer condition is placed on an object, a client must be within the same match to view the object. + /// + [CreateAssetMenu(menuName = "FishNet/Observers/Match Condition", fileName = "New Match Condition")] + public class MatchCondition : ObserverCondition + { + #region Private. + /// + /// + /// + private static Dictionary> _matchConnections = new Dictionary>(); + /// + /// Matches and connections in each match. + /// + public static IReadOnlyDictionary> MatchConnections => _matchConnections; + /// + /// + /// + /// //todo this needs to hold hashset so conns can be in multiple matches. + private static Dictionary _connectionMatch = new Dictionary(); + /// + /// Match a connection is in. + /// + public static IReadOnlyDictionary ConnectionMatch => _connectionMatch; + /// + /// + /// + private static Dictionary> _matchObjects = new Dictionary>(); + /// + /// Matches and connections in each match. + /// + public static IReadOnlyDictionary> MatchObjects => _matchObjects; + /// + /// + /// + /// //todo this needs to hold hashset so conns can be in multiple matches. + private static Dictionary _objectMatch = new Dictionary(); + /// + /// Match a connection is in. + /// + public static IReadOnlyDictionary ObjectMatch => _objectMatch; + #endregion + + public void ConditionConstructor() { } + + #region Add to match NetworkConnection. + /// + /// Adds a connection to a match. + /// + /// Match to add conn to. + /// Connection to add to match. + /// NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used. + /// True to replace other matches with the new match. + public static void AddToMatch(int match, NetworkConnection conn, NetworkManager manager = null, bool replaceMatch = false) + { + if (replaceMatch) + RemoveFromMatchWithoutRebuild(conn, manager); + + HashSet results; + if (!_matchConnections.TryGetValueIL2CPP(match, out results)) + { + results = new HashSet(); + _matchConnections.Add(match, results); + } + + bool r = results.Add(conn); + _connectionMatch[conn] = match; + if (r) + FinalizeChange(match, results, manager); + } + /// + /// Adds connections to a match. + /// + /// Match to add conns to. + /// Connections to add to match. + /// NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used. + /// True to replace other matches with the new match. + public static void AddToMatch(int match, NetworkConnection[] conns, NetworkManager manager = null, bool replaceMatch = false) + { + AddToMatch(match, conns.ToList(), manager, replaceMatch); + } + /// + /// Adds connections to a match. + /// + /// Match to add conns to. + /// Connections to add to match. + /// NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used. + /// True to replace other matches with the new match. + public static void AddToMatch(int match, List conns, NetworkManager manager = null, bool replaceMatch = false) + { + if (replaceMatch) + { + foreach (NetworkConnection nc in conns) + RemoveFromMatchWithoutRebuild(nc, manager); + } + + HashSet results; + if (!_matchConnections.TryGetValueIL2CPP(match, out results)) + { + results = new HashSet(); + _matchConnections.Add(match, results); + } + + bool r = false; + for (int i = 0; i < conns.Count; i++) + { + NetworkConnection c = conns[i]; + r |= results.Add(c); + _connectionMatch[c] = match; + } + + if (r) + FinalizeChange(match, results, manager); + } + #endregion + + #region Add to match NetworkObject. + /// + /// Adds an object to a match. + /// + /// Match to add conn to. + /// Connection to add to match. + /// NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used. + /// True to replace other matches with the new match. + public static void AddToMatch(int match, NetworkObject nob, NetworkManager manager = null, bool replaceMatch = false) + { + if (replaceMatch) + RemoveFromMatchWithoutRebuild(nob, manager); + + HashSet results; + if (!_matchObjects.TryGetValueIL2CPP(match, out results)) + { + results = new HashSet(); + _matchObjects.Add(match, results); + } + + bool r = results.Add(nob); + _objectMatch[nob] = match; + + if (r) + FinalizeChange(match, results, nob, manager); + } + /// + /// Adds objects to a match. + /// + /// Match to add conns to. + /// Connections to add to match. + /// NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used. + /// True to replace other matches with the new match. + public static void AddToMatch(int match, NetworkObject[] nobs, NetworkManager manager = null, bool replaceMatch = false) + { + AddToMatch(match, nobs.ToList(), manager, replaceMatch); + } + /// + /// Adds objects to a match. + /// + /// Match to add conns to. + /// Connections to add to match. + /// NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used. + /// True to replace other matches with the new match. + public static void AddToMatch(int match, List nobs, NetworkManager manager = null, bool replaceMatch = false) + { + if (replaceMatch) + { + foreach (NetworkObject n in nobs) + RemoveFromMatchWithoutRebuild(n, manager); + } + + HashSet results; + if (!_matchObjects.TryGetValueIL2CPP(match, out results)) + { + results = new HashSet(); + _matchObjects.Add(match, results); + } + + bool r = false; + for (int i = 0; i < nobs.Count; i++) + { + NetworkObject n = nobs[i]; + r |= results.Add(n); + _objectMatch[n] = match; + } + + if (r) + FinalizeChange(match, results, nobs, manager); + } + #endregion + + #region Remove from match NetworkConnection. + /// + /// Removes a connection from any match without rebuilding observers. + /// + /// Connection to remove from matches. + /// NetworkManager connection belongs to. This is not currently used. + internal static bool RemoveFromMatchWithoutRebuild(NetworkConnection conn, NetworkManager manager) + { + bool removed = false; + //If found to be in a match. + if (_connectionMatch.TryGetValueIL2CPP(conn, out int match)) + { + //If match is found. + if (_matchConnections.TryGetValue(match, out HashSet conns)) + { + removed |= conns.Remove(conn); + //If no more in hashset remove match. + if (conns.Count == 0) + _matchConnections.Remove(match); + } + } + + //Remove from connectionMatch. + _connectionMatch.Remove(conn); + return removed; + } + /// + /// Removes a connection from all matches. + /// + /// NetworkConnection to remove. + /// NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used. + public static void RemoveFromMatch(NetworkConnection conn, NetworkManager manager) + { + bool removed = RemoveFromMatchWithoutRebuild(conn, manager); + if (removed) + GetServerObjects(manager).RebuildObservers(); + } + /// + /// Removes a connection from a match. + /// + /// Match to remove conn from. + /// Connection to remove from match. + /// NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void RemoveFromMatch(int match, NetworkConnection conn, NetworkManager manager) + { + if (_matchConnections.TryGetValueIL2CPP(match, out HashSet results)) + { + bool r = results.Remove(conn); + _connectionMatch.Remove(conn); + if (r) + FinalizeChange(match, results, manager); + } + } + /// + /// Removes connections from a match. + /// + /// Match to remove conns from. + /// Connections to remove from match. + /// NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void RemoveFromMatch(int match, NetworkConnection[] conns, NetworkManager manager) + { + if (_matchConnections.TryGetValueIL2CPP(match, out HashSet results)) + { + bool r = false; + for (int i = 0; i < conns.Length; i++) + { + NetworkConnection c = conns[i]; + r |= results.Remove(c); + _connectionMatch.Remove(c); + } + + if (r) + FinalizeChange(match, results, manager); + } + } + /// + /// Removes connections from a match. + /// + /// Match to remove conns from. + /// Connections to remove from match. + /// NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void RemoveFromMatch(int match, List conns, NetworkManager manager) + { + if (_matchConnections.TryGetValueIL2CPP(match, out HashSet results)) + { + bool r = false; + for (int i = 0; i < conns.Count; i++) + { + NetworkConnection c = conns[i]; + r |= results.Remove(c); + _connectionMatch.Remove(c); + } + + if (r) + FinalizeChange(match, results, manager); + } + } + #endregion + + #region Remove from match NetworkObject. + /// + /// Removes a network object from any match without rebuilding observers. + /// + /// NetworkObject to remove. + /// Manager which the network object belongs to. This value is not yet used. + internal static bool RemoveFromMatchWithoutRebuild(NetworkObject nob, NetworkManager manager = null) + { + bool removed = false; + //If found to be in a match. + if (_objectMatch.TryGetValueIL2CPP(nob, out int match)) + { + //If match is found. + if (_matchObjects.TryGetValue(match, out HashSet nobs)) + { + removed |= nobs.Remove(nob); + //If no more in hashset remove match. + if (nobs.Count == 0) + _matchObjects.Remove(match); + } + } + + //Remove from connectionMatch. + _objectMatch.Remove(nob); + return removed; + } + /// + /// Removes nob from all matches. + /// + /// NetworkObject to remove. + /// NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used. + public static void RemoveFromMatch(NetworkObject nob, NetworkManager manager = null) + { + bool removed = RemoveFromMatchWithoutRebuild(nob, manager); + if (removed) + GetServerObjects(manager).RebuildObservers(nob); + } + /// + /// Removes a network object from all matches. + /// + /// NetworkObjects to remove. + /// NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used. + public static void RemoveFromMatch(NetworkObject[] nobs, NetworkManager manager = null) + { + RemoveFromMatch(nobs.ToList(), manager); + } + /// + /// Removes network objects from all matches. + /// + /// NetworkObjects to remove. + /// NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used. + public static void RemoveFromMatch(List nobs, NetworkManager manager = null) + { + bool removed = false; + foreach (NetworkObject n in nobs) + removed |= RemoveFromMatchWithoutRebuild(n, manager); + + if (removed) + GetServerObjects(manager).RebuildObservers(nobs); + } + /// + /// Removes a network object from a match. + /// + /// Match to remove conn from. + /// NetworkObject to remove from match. + /// NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void RemoveFromMatch(int match, NetworkObject nob, NetworkManager manager = null) + { + if (_matchObjects.TryGetValueIL2CPP(match, out HashSet results)) + { + bool r = results.Remove(nob); + _objectMatch.Remove(nob); + if (r) + FinalizeChange(match, results, nob, manager); + } + } + /// + /// Removes network objects from a match. + /// + /// Match to remove conns from. + /// NetworkObjects to remove from match. + /// NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void RemoveFromMatch(int match, NetworkObject[] nobs, NetworkManager manager = null) + { + if (_matchObjects.TryGetValueIL2CPP(match, out HashSet results)) + { + bool r = false; + for (int i = 0; i < nobs.Length; i++) + { + NetworkObject n = nobs[i]; + r |= results.Remove(n); + _objectMatch.Remove(n); + } + + if (r) + FinalizeChange(match, results, nobs, manager); + } + } + /// + /// Removes network objects from a match. + /// + /// Match to remove conns from. + /// NetworkObjects to remove from match. + /// NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void RemoveFromMatch(int match, List nobs, NetworkManager manager = null) + { + if (_matchObjects.TryGetValueIL2CPP(match, out HashSet results)) + { + bool r = false; + for (int i = 0; i < nobs.Count; i++) + { + NetworkObject n = nobs[i]; + r |= results.Remove(n); + _objectMatch.Remove(n); + } + + if (r) + FinalizeChange(match, results, nobs, manager); + } + } + #endregion + + #region FinalizeChange NetworkConnection. + /// + /// Finalizes changes to observers. + /// + private static void FinalizeChange(int match, HashSet remainingConnsInMatch, NetworkManager manager) + { + if (remainingConnsInMatch.Count == 0) + _matchConnections.Remove(match); + + /* Observers on all objects and all conditions have to be rebuilt. + * This is because the connection changing matches could + * require the connection to be visible for other players in the match, + * as well make other connections in the same match visible. + * But also make all the objects not associated with connections + * of that match visible. In result to tick all of those boxes + * all objects need to be rebuilt for all connections. */ + GetServerObjects(manager).RebuildObservers(); + } + #endregion + + #region FinalizeChange NetworkObject. + /// + /// Finalizes changes to observers. + /// + private static void FinalizeChange(int match, HashSet results, List nobs, NetworkManager manager) + { + ListCache cache = ListCaches.GetNetworkObjectCache(); + cache.AddValues(nobs); + FinalizeChange(match, results, cache, manager); + ListCaches.StoreCache(cache); + } + /// + /// Finalizes changes to observers. + /// + private static void FinalizeChange(int match, HashSet results, NetworkObject[] nobs, NetworkManager manager) + { + ListCache cache = ListCaches.GetNetworkObjectCache(); + cache.AddValues(nobs); + FinalizeChange(match, results, cache, manager); + ListCaches.StoreCache(cache); + } + /// + /// Finalizes changes to observers. + /// + private static void FinalizeChange(int match, HashSet results, NetworkObject nob, NetworkManager manager) + { + ListCache cache = ListCaches.GetNetworkObjectCache(); + cache.AddValue(nob); + FinalizeChange(match, results, cache, manager); + ListCaches.StoreCache(cache); + } + /// + /// Finalizes changes to observers. + /// + private static void FinalizeChange(int match, HashSet results, ListCache nobs, NetworkManager manager) + { + if (results.Count == 0) + _matchConnections.Remove(match); + + GetServerObjects(manager).RebuildObservers(nobs); + } + #endregion + + /// + /// Returns if the object which this condition resides should be visible to connection. + /// + /// Connection which the condition is being checked for. + /// True if the connection currently has visibility of this object. + /// True if the condition was not processed. This can be used to skip processing for performance. While output as true this condition result assumes the previous ConditionMet value. + public override bool ConditionMet(NetworkConnection connection, bool alreadyAdded, out bool notProcessed) + { + //If here then checks are being processed. + notProcessed = false; + NetworkConnection owner = base.NetworkObject.Owner; + /* If object is owned then check if owner + * and connection share a match. */ + if (owner.IsValid) + { + //Connection isn't in a match. + if (!_connectionMatch.TryGetValueIL2CPP(connection, out int match)) + { + //Return if this owner is also not in a match. + return !_connectionMatch.TryGetValueIL2CPP(owner, out int _); + } + //Match isn't found. + if (!_matchConnections.TryGetValueIL2CPP(match, out HashSet conns)) + return false; + //If owner is in same match return true. + return conns.Contains(owner); + } + /* If no owner see if the object is in a match and if so + * then compare that. */ + else + { + //Object isn't in a match. + if (!_objectMatch.TryGetValueIL2CPP(base.NetworkObject, out int objectMatch)) + return true; + /* See if connection is in the same match as the object. + * If connection isn't in a match then it fails. */ + if (!_connectionMatch.TryGetValueIL2CPP(connection, out int connectionMatch)) + return false; + return (connectionMatch == objectMatch); + } + } + + + /// + /// Returns which ServerObjects to rebuild observers on. + /// + /// + /// + private static ServerObjects GetServerObjects(NetworkManager manager) + { + return (manager == null) ? InstanceFinder.ServerManager.Objects : manager.ServerManager.Objects; + } + + + /* //todo this needs to be passing in the network manager to clear on, + * otherwise only a single instance of NM is supported. + * Users are already forced to specify which NM to add + * matches for but the functionality separating different NMs in relation + * to such isn't done yet. */ + /// + /// Clears all match information without rebuilding. + /// + internal static void ClearMatchesWithoutRebuilding() + { + _connectionMatch.Clear(); + _matchConnections.Clear(); + _objectMatch.Clear(); + _matchObjects.Clear(); + } + + + /// + /// True if the condition requires regular updates. + /// + /// + public override bool Timed() + { + return false; + } + + /// + /// Clones referenced ObserverCondition. This must be populated with your conditions settings. + /// + /// + public override ObserverCondition Clone() + { + MatchCondition copy = ScriptableObject.CreateInstance(); + copy.ConditionConstructor(); + return copy; + } + } +} diff --git a/Assets/FishNet/Runtime/Observing/Conditions/MatchCondition.cs.meta b/Assets/FishNet/Runtime/Observing/Conditions/MatchCondition.cs.meta new file mode 100644 index 0000000..60e6e9e --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/MatchCondition.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5afdd6c2de1c76f4faa6840cc29fda8a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Observing/Conditions/OwnerOnlyCondition.cs b/Assets/FishNet/Runtime/Observing/Conditions/OwnerOnlyCondition.cs new file mode 100644 index 0000000..bd3a7ec --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/OwnerOnlyCondition.cs @@ -0,0 +1,47 @@ +using FishNet.Connection; +using FishNet.Observing; +using UnityEngine; + +namespace FishNet.Component.Observing +{ + /// + /// This condition makes an object only visible to the owner. + /// + [CreateAssetMenu(menuName = "FishNet/Observers/Owner Only Condition", fileName = "New Owner Only Condition")] + public class OwnerOnlyCondition : ObserverCondition + { + + /// + /// Returns if the object which this condition resides should be visible to connection. + /// + /// Connection which the condition is being checked for. + /// True if the connection currently has visibility of this object. + /// True if the condition was not processed. This can be used to skip processing for performance. While output as true this condition result assumes the previous ConditionMet value. + public override bool ConditionMet(NetworkConnection connection, bool currentlyAdded, out bool notProcessed) + { + notProcessed = false; + /* Returning false immediately indicates no connection will + * meet this condition. */ + return false; + } + + /// + /// True if the condition requires regular updates. + /// + /// + public override bool Timed() + { + return false; + } + + /// + /// Clones referenced ObserverCondition. This must be populated with your conditions settings. + /// + /// + public override ObserverCondition Clone() + { + OwnerOnlyCondition copy = ScriptableObject.CreateInstance(); + return copy; + } + } +} diff --git a/Assets/FishNet/Runtime/Observing/Conditions/OwnerOnlyCondition.cs.meta b/Assets/FishNet/Runtime/Observing/Conditions/OwnerOnlyCondition.cs.meta new file mode 100644 index 0000000..3a99b3d --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/OwnerOnlyCondition.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1ca3d8a36a10fd344806a2df999f3eda +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Observing/Conditions/SceneCondition.cs b/Assets/FishNet/Runtime/Observing/Conditions/SceneCondition.cs new file mode 100644 index 0000000..f049814 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/SceneCondition.cs @@ -0,0 +1,88 @@ +using FishNet.Connection; +using FishNet.Observing; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace FishNet.Component.Observing +{ + + /// + /// When this observer condition is placed on an object, a client must be within the same scene to view the object. + /// + [CreateAssetMenu(menuName = "FishNet/Observers/Scene Condition", fileName = "New Scene Condition")] + public class SceneCondition : ObserverCondition + { + #region Serialized. + ///// + ///// True to synchronize which scene the object was spawned in to clients. When true this object will be moved to the clients equivelant of the scene it was spawned in on the server. This setting does not continously move this object to the same scene. + ///// + //[Tooltip("True to synchronize which scene the object was spawned in to clients. When true this object will be moved to the clients equivelant of the scene it was spawned in on the server. This setting does not continously move this object to the same scene.")] + //[SerializeField] + //private bool _synchronizeScene; + #endregion + + public void ConditionConstructor() + { + //_synchronizeScene = synchronizeScene; + } + + /// + /// Returns if the object which this condition resides should be visible to connection. + /// + /// Connection which the condition is being checked for. + /// True if the connection currently has visibility of this object. + /// True if the condition was not processed. This can be used to skip processing for performance. While output as true this condition result assumes the previous ConditionMet value. + public override bool ConditionMet(NetworkConnection connection, bool currentlyAdded, out bool notProcessed) + { + notProcessed = false; + /* If this objects connection is valid then check if + * connection and this objects owner shares any scenes. + * Don't check if the object resides in the same scene + * because thats not reliable as server might be moving + * objects. */ + if (base.NetworkObject.Owner.IsValid) + { + foreach (Scene s in base.NetworkObject.Owner.Scenes) + { + //Scenes match. + if (connection.Scenes.Contains(s)) + return true; + } + + //Fall through, no scenes shared. + return false; + } + /* If there is no owner as a fallback see if + * the connection is in the same scene as this object. */ + else + { + /* When there is no owner only then is the gameobject + * scene checked. That's the only way to know at this point. */ + return connection.Scenes.Contains(base.NetworkObject.gameObject.scene); + } + } + + /// + /// True if the condition requires regular updates. + /// + /// + public override bool Timed() + { + return false; + } + + + /// + /// Clones referenced ObserverCondition. This must be populated with your conditions settings. + /// + /// + public override ObserverCondition Clone() + { + SceneCondition copy = ScriptableObject.CreateInstance(); + //copy.ConditionConstructor(_synchronizeScene); + copy.ConditionConstructor(); + return copy; + } + + } +} diff --git a/Assets/FishNet/Runtime/Observing/Conditions/SceneCondition.cs.meta b/Assets/FishNet/Runtime/Observing/Conditions/SceneCondition.cs.meta new file mode 100644 index 0000000..edff5be --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/SceneCondition.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fab85d1c51ee2c344b7dd914dc262ec4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects.meta b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects.meta new file mode 100644 index 0000000..35a6fe4 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3d6d71fdcee27584d8d47bef4455a5f9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/DistanceCondition.asset b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/DistanceCondition.asset new file mode 100644 index 0000000..10a9cb4 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/DistanceCondition.asset @@ -0,0 +1,18 @@ +%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: 7c3e28fa2e37d1d41b4f63c8a0cc2553, type: 3} + m_Name: DistanceCondition + m_EditorClassIdentifier: + NetworkObject: {fileID: 0} + _maximumDistance: 10 + _hideDistancePercent: 0.1 + _updateFrequency: 0 diff --git a/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/DistanceCondition.asset.meta b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/DistanceCondition.asset.meta new file mode 100644 index 0000000..1ba0a6e --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/DistanceCondition.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5f33eb0e5b83b5546822cfe42a305657 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/HostOnlyCondition.asset b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/HostOnlyCondition.asset new file mode 100644 index 0000000..939ae62 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/HostOnlyCondition.asset @@ -0,0 +1,15 @@ +%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: aa62c0af0c0a4da46b03309dcd3858c3, type: 3} + m_Name: HostOnlyCondition + m_EditorClassIdentifier: + NetworkObject: {fileID: 0} diff --git a/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/HostOnlyCondition.asset.meta b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/HostOnlyCondition.asset.meta new file mode 100644 index 0000000..497d938 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/HostOnlyCondition.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9ff842b44ec59314d9efcecbcdbaac04 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/MatchCondition.asset b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/MatchCondition.asset new file mode 100644 index 0000000..0d44d4f --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/MatchCondition.asset @@ -0,0 +1,15 @@ +%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: 5afdd6c2de1c76f4faa6840cc29fda8a, type: 3} + m_Name: MatchCondition + m_EditorClassIdentifier: + NetworkObject: {fileID: 0} diff --git a/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/MatchCondition.asset.meta b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/MatchCondition.asset.meta new file mode 100644 index 0000000..39dd07a --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/MatchCondition.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: efcd7f0dfd341ed4e8671079e91e0544 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/OwnerOnlyCondition.asset b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/OwnerOnlyCondition.asset new file mode 100644 index 0000000..625e5f1 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/OwnerOnlyCondition.asset @@ -0,0 +1,15 @@ +%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: 1ca3d8a36a10fd344806a2df999f3eda, type: 3} + m_Name: OwnerOnlyCondition + m_EditorClassIdentifier: + NetworkObject: {fileID: 0} diff --git a/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/OwnerOnlyCondition.asset.meta b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/OwnerOnlyCondition.asset.meta new file mode 100644 index 0000000..389066f --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/OwnerOnlyCondition.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bcf92670d91dbb74dad77c56b9b8712e +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/SceneCondition.asset b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/SceneCondition.asset new file mode 100644 index 0000000..0877337 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/SceneCondition.asset @@ -0,0 +1,17 @@ +%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: fab85d1c51ee2c344b7dd914dc262ec4, type: 3} + m_Name: SceneCondition + m_EditorClassIdentifier: + NetworkObject: {fileID: 0} + _synchronizeScene: 1 + _timed: 0 diff --git a/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/SceneCondition.asset.meta b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/SceneCondition.asset.meta new file mode 100644 index 0000000..6cc77dd --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/SceneCondition.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2033f54fd2794464bae08fa5a55c8996 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Observing/HostVisibilityUpdateTypes.cs b/Assets/FishNet/Runtime/Observing/HostVisibilityUpdateTypes.cs new file mode 100644 index 0000000..4699aca --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/HostVisibilityUpdateTypes.cs @@ -0,0 +1,17 @@ + +namespace FishNet.Observing +{ + [System.Flags] + public enum HostVisibilityUpdateTypes : byte + { + /// + /// Include this flag to update manager. + /// + Manager = 1, + /// + /// Include this flag to update spawned. + /// + Spawned = 2, + } + +} diff --git a/Assets/FishNet/Runtime/Observing/HostVisibilityUpdateTypes.cs.meta b/Assets/FishNet/Runtime/Observing/HostVisibilityUpdateTypes.cs.meta new file mode 100644 index 0000000..671c6cb --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/HostVisibilityUpdateTypes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dc90636a96cf14d47812768a9ce3a4d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Observing/NetworkObserver.cs b/Assets/FishNet/Runtime/Observing/NetworkObserver.cs new file mode 100644 index 0000000..7ad3657 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/NetworkObserver.cs @@ -0,0 +1,404 @@ +using FishNet.Connection; +using FishNet.Documenting; +using FishNet.Object; +using FishNet.Transporting; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; +using UnityEngine.Serialization; + +namespace FishNet.Observing +{ + /// + /// Controls which clients can see and get messages for an object. + /// + [DisallowMultipleComponent] + [AddComponentMenu("FishNet/Component/NetworkObserver")] + public sealed class NetworkObserver : MonoBehaviour + { + #region Types. + /// + /// How ObserverManager conditions are used. + /// + public enum ConditionOverrideType + { + /// + /// Keep current conditions, add new conditions from manager. + /// + AddMissing = 1, + /// + /// Replace current conditions with manager conditions. + /// + UseManager = 2, + /// + /// Keep current conditions, ignore manager conditions. + /// + IgnoreManager = 3, + } + #endregion + + #region Serialized. + /// + /// + /// + [Tooltip("How ObserverManager conditions are used.")] + [SerializeField] + private ConditionOverrideType _overrideType = ConditionOverrideType.IgnoreManager; + /// + /// How ObserverManager conditions are used. + /// + public ConditionOverrideType OverrideType + { + get => _overrideType; + internal set => _overrideType = value; + } + + /// + /// + /// + [Tooltip("True to update visibility for clientHost based on if they are an observer or not.")] + [FormerlySerializedAs("_setHostVisibility")] + [SerializeField] + private bool _updateHostVisibility = true; + /// + /// True to update visibility for clientHost based on if they are an observer or not. + /// + public bool UpdateHostVisibility + { + get => _updateHostVisibility; + private set => _updateHostVisibility = value; + } + /// + /// + /// + [Tooltip("Conditions connections must met to be added as an observer. Multiple conditions may be used.")] + [SerializeField] + internal List _observerConditions = new List(); + /// + /// Conditions connections must met to be added as an observer. Multiple conditions may be used. + /// + public IReadOnlyList ObserverConditions => _observerConditions; + [APIExclude] +#if MIRROR + public List ObserverConditionsInternal +#else + internal List ObserverConditionsInternal +#endif + { + get => _observerConditions; + set => _observerConditions = value; + } + #endregion + + #region Private. + /// + /// Conditions under this component which are timed. + /// + private List _timedConditions = new List(); + /// + /// Connections which have all non-timed conditions met. + /// + private HashSet _nonTimedMet = new HashSet(); + /// + /// NetworkObject this belongs to. + /// + private NetworkObject _networkObject; + /// + /// Becomes true when registered with ServerObjects as Timed observers. + /// + private bool _registeredAsTimed; + /// + /// True if already pre-initialized. + /// + private bool _preintiialized; + /// + /// True if ParentNetworkObject was visible last iteration. + /// This value will also be true if there is no ParentNetworkObject. + /// + private bool _lastParentVisible; + #endregion + + private void OnEnable() + { + if (_networkObject != null && _networkObject.IsServer) + RegisterTimedConditions(); + } + private void OnDisable() + { + if (_networkObject != null && _networkObject.IsDeinitializing) + { + _lastParentVisible = false; + _nonTimedMet.Clear(); + UnregisterTimedConditions(); + } + } + private void OnDestroy() + { + if (_networkObject != null) + UnregisterTimedConditions(); + } + + internal void Deinitialize() + { + if (_networkObject != null && _networkObject.IsDeinitializing) + { + _networkObject.ServerManager.OnRemoteConnectionState -= ServerManager_OnRemoteConnectionState; + UnregisterTimedConditions(); + } + } + + /// + /// Initializes this script for use. + /// + /// + internal void PreInitialize(NetworkObject networkObject) + { + if (!_preintiialized) + { + _preintiialized = true; + _networkObject = networkObject; + bool ignoringManager = (OverrideType == ConditionOverrideType.IgnoreManager); + + //Check to override SetHostVisibility. + if (!ignoringManager) + UpdateHostVisibility = networkObject.ObserverManager.UpdateHostVisibility; + + bool observerFound = false; + for (int i = 0; i < _observerConditions.Count; i++) + { + if (_observerConditions[i] != null) + { + observerFound = true; + + /* Make an instance of each condition so values are + * not overwritten when the condition exist more than + * once in the scene. Double edged sword of using scriptable + * objects for conditions. */ + _observerConditions[i] = _observerConditions[i].Clone(); + ObserverCondition oc = _observerConditions[i]; + oc.InitializeOnce(_networkObject); + //If timed also register as containing timed conditions. + if (oc.Timed()) + _timedConditions.Add(oc); + } + else + { + _observerConditions.RemoveAt(i); + i--; + } + } + + //No observers specified, do not need to take further action. + if (!observerFound) + return; + + _networkObject.ServerManager.OnRemoteConnectionState += ServerManager_OnRemoteConnectionState; + } + + RegisterTimedConditions(); + } + + /// + /// Returns a condition if found within Conditions. + /// + /// + public ObserverCondition GetObserverCondition() where T : ObserverCondition + { + /* Do not bother setting local variables, + * condition collections aren't going to be long + * enough to make doing so worth while. */ + + System.Type conditionType = typeof(T); + for (int i = 0; i < _observerConditions.Count; i++) + { + if (_observerConditions[i].GetType() == conditionType) + return _observerConditions[i]; + } + + //Fall through, not found. + return null; + } + + /// + /// Returns ObserverStateChange by comparing conditions for a connection. + /// + /// True if added to Observers. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ObserverStateChange RebuildObservers(NetworkConnection connection, bool timedOnly) + { + bool currentlyAdded = (_networkObject.Observers.Contains(connection)); + //True if all conditions are met. + bool allConditionsMet = true; + + //Only need to check beyond this if conditions exist. + if (_observerConditions.Count > 0) + { + /* If cnnection is owner then they can see the object. */ + bool notOwner = (connection != _networkObject.Owner); + + /* Only check conditions if not owner. Owner will always + * have visibility. */ + if (notOwner) + { + bool parentVisible = true; + if (_networkObject.ParentNetworkObject != null) + { + parentVisible = _networkObject.ParentNetworkObject.Observers.Contains(connection); + /* If parent is visible but was not previously + * then unset timedOnly to make sure all conditions + * are checked again. This ensures that the _nonTimedMet + * collection is updated. */ + if (parentVisible && !_lastParentVisible) + timedOnly = false; + _lastParentVisible = parentVisible; + } + + //If parent is not visible no further checks are required. + if (!parentVisible) + { + allConditionsMet = false; + } + //Parent is visible, perform checks. + else + { + //True if connection starts with meeting non-timed conditions. + bool startNonTimedMet = _nonTimedMet.Contains(connection); + /* If a timed update an1d nonTimed + * have not been met then there's + * no reason to check timed. */ + if (timedOnly && !startNonTimedMet) + { + allConditionsMet = false; + } + else + { + //Becomes true if a non-timed condition fails. + bool nonTimedMet = true; + + List collection = (timedOnly) ? _timedConditions : _observerConditions; + for (int i = 0; i < collection.Count; i++) + { + ObserverCondition condition = collection[i]; + /* If any observer returns removed then break + * from loop and return removed. If one observer has + * removed then there's no reason to iterate + * the rest. + * + * A condition is automatically met if it's not enabled. */ + bool notProcessed = false; + bool conditionMet = (!condition.GetIsEnabled() || condition.ConditionMet(connection, currentlyAdded, out notProcessed)); + + if (notProcessed) + conditionMet = currentlyAdded; + + //Condition not met. + if (!conditionMet) + { + allConditionsMet = false; + if (!condition.Timed()) + nonTimedMet = false; + break; + } + } + + //If all conditions are being checked and nonTimedMet has updated. + if (!timedOnly && (startNonTimedMet != nonTimedMet)) + { + if (nonTimedMet) + _nonTimedMet.Add(connection); + else + _nonTimedMet.Remove(connection); + } + } + } + } + } + + //If all conditions met. + if (allConditionsMet) + return ReturnPassedConditions(currentlyAdded); + else + return ReturnFailedCondition(currentlyAdded); + } + + /// + /// Registers timed observer conditions. + /// + private void RegisterTimedConditions() + { + if (_timedConditions.Count == 0) + return; + //Already registered or no timed conditions. + if (_registeredAsTimed) + return; + + _registeredAsTimed = true; + _networkObject.NetworkManager.ServerManager.Objects.AddTimedNetworkObserver(_networkObject); + } + + /// + /// Unregisters timed conditions. + /// + private void UnregisterTimedConditions() + { + if (_timedConditions.Count == 0) + return; + if (!_registeredAsTimed) + return; + + _registeredAsTimed = false; + _networkObject.NetworkManager.ServerManager.Objects.RemoveTimedNetworkObserver(_networkObject); + } + + /// + /// Returns an ObserverStateChange when a condition fails. + /// + /// + /// + private ObserverStateChange ReturnFailedCondition(bool currentlyAdded) + { + if (currentlyAdded) + return ObserverStateChange.Removed; + else + return ObserverStateChange.Unchanged; + } + + /// + /// Returns an ObserverStateChange when all conditions pass. + /// + /// + /// + private ObserverStateChange ReturnPassedConditions(bool currentlyAdded) + { + if (currentlyAdded) + return ObserverStateChange.Unchanged; + else + return ObserverStateChange.Added; + } + + /// + /// Called when a remote client state changes with the server. + /// + private void ServerManager_OnRemoteConnectionState(NetworkConnection conn, RemoteConnectionStateArgs arg2) + { + if (arg2.ConnectionState == RemoteConnectionState.Stopped) + _nonTimedMet.Remove(conn); + } + + /// + /// Sets a new value for UpdateHostVisibility. + /// This does not immediately update renderers. + /// You may need to combine with NetworkObject.SetRenderersVisible(bool). + /// + /// New value. + public void SetUpdateHostVisibility(bool value) + { + //Unchanged. + if (value == UpdateHostVisibility) + return; + + UpdateHostVisibility = value; + } + + } +} diff --git a/Assets/FishNet/Runtime/Observing/NetworkObserver.cs.meta b/Assets/FishNet/Runtime/Observing/NetworkObserver.cs.meta new file mode 100644 index 0000000..0809554 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/NetworkObserver.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c71fd7f855ec523429999fc4e14a1928 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Observing/ObserverCondition.cs b/Assets/FishNet/Runtime/Observing/ObserverCondition.cs new file mode 100644 index 0000000..0c0fad4 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/ObserverCondition.cs @@ -0,0 +1,81 @@ +using FishNet.Connection; +using FishNet.Managing.Server; +using FishNet.Object; +using UnityEngine; + +namespace FishNet.Observing +{ + /// + /// Condition a connection must meet to be added as an observer. + /// This class can be inherited from for custom conditions. + /// + public abstract class ObserverCondition : ScriptableObject + { + #region Public. + /// + /// NetworkObject this condition is for. + /// + [HideInInspector] + public NetworkObject NetworkObject; + #endregion + + #region Private. + /// + /// True if this condition is enabled. + /// + private bool _isEnabled = true; + /// + /// Gets the enabled state of this condition. + /// + /// + public bool GetIsEnabled() => _isEnabled; + /// + /// Sets the enabled state of this condition. + /// If the state has changed observers will be rebuilt + /// for this object. + /// + /// + public void SetIsEnabled(bool value) + { + if (value == GetIsEnabled()) + return; + + _isEnabled = value; + //No object to rebuild for. + if (NetworkObject == null) + return; + + ServerObjects so = NetworkObject?.ServerManager?.Objects; + if (so != null) + so.RebuildObservers(NetworkObject); + } + #endregion + + /// + /// Initializes this script for use. + /// + /// + public virtual void InitializeOnce(NetworkObject networkObject) + { + NetworkObject = networkObject; + } + /// + /// Returns if the object which this condition resides should be visible to connection. + /// + /// Connection which the condition is being checked for. + /// True if the connection currently has visibility of this object. + /// True if the condition was not processed. This can be used to skip processing for performance. While output as true this condition result assumes the previous ConditionMet value. + public abstract bool ConditionMet(NetworkConnection connection, bool currentlyAdded, out bool notProcessed); + /// + /// True if the condition requires regular updates. + /// + /// + public abstract bool Timed(); + /// + /// Creates a clone of this condition to be instantiated. + /// + /// + public abstract ObserverCondition Clone(); + + } +} diff --git a/Assets/FishNet/Runtime/Observing/ObserverCondition.cs.meta b/Assets/FishNet/Runtime/Observing/ObserverCondition.cs.meta new file mode 100644 index 0000000..08b27fe --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/ObserverCondition.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7d496d4febcb07f4abbdc081eaa99234 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Observing/ObserverStateChange.cs b/Assets/FishNet/Runtime/Observing/ObserverStateChange.cs new file mode 100644 index 0000000..d783cc3 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/ObserverStateChange.cs @@ -0,0 +1,12 @@ +namespace FishNet.Observing +{ + /// + /// States which observer(s) can change to. + /// + internal enum ObserverStateChange : byte + { + Unchanged = 0, + Added = 1, + Removed = 2 + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Observing/ObserverStateChange.cs.meta b/Assets/FishNet/Runtime/Observing/ObserverStateChange.cs.meta new file mode 100644 index 0000000..4529100 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/ObserverStateChange.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a4a8dca28b7d84548a918c5c32f684ad +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins.meta b/Assets/FishNet/Runtime/Plugins.meta new file mode 100644 index 0000000..0e82d79 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fc3a5558e3b83cc41a1f450b12e9ac14 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/ColliderRollback.meta b/Assets/FishNet/Runtime/Plugins/ColliderRollback.meta new file mode 100644 index 0000000..fc14e86 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/ColliderRollback.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8a39159d806d05c4a9eb7a7a389d8396 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/ColliderRollback/Attributions.txt b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Attributions.txt new file mode 100644 index 0000000..b548ed0 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Attributions.txt @@ -0,0 +1,5 @@ +Attribution to https://assetstore.unity.com/packages/3d/environments/landscapes/low-poly-simple-nature-pack-162153 +"SimpleNaturePack" + +Attribution to Kenny.nl +"Weapon Pack" \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/ColliderRollback/Attributions.txt.meta b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Attributions.txt.meta new file mode 100644 index 0000000..a501e44 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Attributions.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0b87cdb0a34a9254ab77396878d3679e +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/ColliderRollback/LICENSE.txt b/Assets/FishNet/Runtime/Plugins/ColliderRollback/LICENSE.txt new file mode 100644 index 0000000..54c6022 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/ColliderRollback/LICENSE.txt @@ -0,0 +1 @@ +This software uses the license for Fish-Networking: https://github.com/FirstGearGames/FishNet/blob/main/LICENSE.md \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/ColliderRollback/LICENSE.txt.meta b/Assets/FishNet/Runtime/Plugins/ColliderRollback/LICENSE.txt.meta new file mode 100644 index 0000000..a551d13 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/ColliderRollback/LICENSE.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: ff1ecc2a24fd9684a862c4b99cfc2fcd +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts.meta b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts.meta new file mode 100644 index 0000000..7084ba5 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d1d5f2c3ff10faa45a45d9cb3b1bfcc1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollback.cs b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollback.cs new file mode 100644 index 0000000..9ba3166 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollback.cs @@ -0,0 +1,23 @@ +using FishNet.Object; +using UnityEngine; + +namespace FishNet.Component.ColliderRollback +{ + + public class ColliderRollback : NetworkBehaviour + { + + + #region Serialized. + /// + /// Objects holding colliders which can rollback. + /// + [Tooltip("Objects holding colliders which can rollback.")] + [SerializeField] + private GameObject[] _colliderParents = new GameObject[0]; + #endregion + + + } + +} diff --git a/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollback.cs.meta b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollback.cs.meta new file mode 100644 index 0000000..6d18cfb --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollback.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 01a271dd97d875347b0cea860df29a9d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackManager.cs b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackManager.cs new file mode 100644 index 0000000..b23c119 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackManager.cs @@ -0,0 +1,51 @@ +using FishNet.Managing; +using FishNet.Managing.Timing; +using FishNet.Transporting; +using System; +using UnityEngine; + +namespace FishNet.Component.ColliderRollback +{ + public class RollbackManager : MonoBehaviour + { + + + + + #region Serialized. + /// + /// + /// + [Tooltip("Maximum time in the past colliders can be rolled back to.")] + [SerializeField] + private float _maximumRollbackTime = 1.25f; + /// + /// Maximum time in the past colliders can be rolled back to. + /// + internal float MaximumRollbackTime => _maximumRollbackTime; + /// + /// + /// + [Tooltip("Interpolation value for the NetworkTransform or object being rolled back.")] + [Range(0, 250)] + [SerializeField] + internal ushort Interpolation = 2; + #endregion + + + + + + /// + /// Initializes this script for use. + /// + /// + internal void InitializeOnce_Internal(NetworkManager manager) + { + + } + + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackManager.cs.meta b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackManager.cs.meta new file mode 100644 index 0000000..c7c510b --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b185516acd802904383e2a5f1a666750 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/Yak.meta b/Assets/FishNet/Runtime/Plugins/Yak.meta new file mode 100644 index 0000000..dcae338 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0bfce5a10f5315248bf3e7eaed92265d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/Yak/CHANGELOG.txt b/Assets/FishNet/Runtime/Plugins/Yak/CHANGELOG.txt new file mode 100644 index 0000000..4f3eea1 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak/CHANGELOG.txt @@ -0,0 +1,2 @@ +1.0.0 + - Initial release. \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/Yak/CHANGELOG.txt.meta b/Assets/FishNet/Runtime/Plugins/Yak/CHANGELOG.txt.meta new file mode 100644 index 0000000..959d54e --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak/CHANGELOG.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 65389c57cd9accf47967cc3e6cb7ac1b +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/Yak/Core.meta b/Assets/FishNet/Runtime/Plugins/Yak/Core.meta new file mode 100644 index 0000000..7f0cd25 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak/Core.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3b79a48228ccfcd4cbf0a0514295abeb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/Yak/Core/ClientSocket.cs b/Assets/FishNet/Runtime/Plugins/Yak/Core/ClientSocket.cs new file mode 100644 index 0000000..e69de29 diff --git a/Assets/FishNet/Runtime/Plugins/Yak/Core/ClientSocket.cs.meta b/Assets/FishNet/Runtime/Plugins/Yak/Core/ClientSocket.cs.meta new file mode 100644 index 0000000..ac679e5 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak/Core/ClientSocket.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 26269fe7187f5da4e957080519ea0f13 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/Yak/Core/CommonSocket.cs b/Assets/FishNet/Runtime/Plugins/Yak/Core/CommonSocket.cs new file mode 100644 index 0000000..e69de29 diff --git a/Assets/FishNet/Runtime/Plugins/Yak/Core/CommonSocket.cs.meta b/Assets/FishNet/Runtime/Plugins/Yak/Core/CommonSocket.cs.meta new file mode 100644 index 0000000..02600b5 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak/Core/CommonSocket.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 63afa30fa0251df44b9496aded55d795 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/Yak/Core/LocalPacket.cs b/Assets/FishNet/Runtime/Plugins/Yak/Core/LocalPacket.cs new file mode 100644 index 0000000..5f28270 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak/Core/LocalPacket.cs @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/Yak/Core/LocalPacket.cs.meta b/Assets/FishNet/Runtime/Plugins/Yak/Core/LocalPacket.cs.meta new file mode 100644 index 0000000..32c8808 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak/Core/LocalPacket.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f52ce359669f91c4d981dc605a8875b7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/Yak/Core/ServerSocket.cs b/Assets/FishNet/Runtime/Plugins/Yak/Core/ServerSocket.cs new file mode 100644 index 0000000..e69de29 diff --git a/Assets/FishNet/Runtime/Plugins/Yak/Core/ServerSocket.cs.meta b/Assets/FishNet/Runtime/Plugins/Yak/Core/ServerSocket.cs.meta new file mode 100644 index 0000000..16d2d4c --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak/Core/ServerSocket.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9181ba25449c96446b966d0bd62e5813 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/Yak/VERSION.txt b/Assets/FishNet/Runtime/Plugins/Yak/VERSION.txt new file mode 100644 index 0000000..afaf360 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak/VERSION.txt @@ -0,0 +1 @@ +1.0.0 \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/Yak/VERSION.txt.meta b/Assets/FishNet/Runtime/Plugins/Yak/VERSION.txt.meta new file mode 100644 index 0000000..afecaf0 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak/VERSION.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3f0e4449aaa7cf0499df1847fdbd5377 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/Yak/Yak.cs b/Assets/FishNet/Runtime/Plugins/Yak/Yak.cs new file mode 100644 index 0000000..e69de29 diff --git a/Assets/FishNet/Runtime/Plugins/Yak/Yak.cs.meta b/Assets/FishNet/Runtime/Plugins/Yak/Yak.cs.meta new file mode 100644 index 0000000..9b936a3 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak/Yak.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 91f4cf5273666764789e6f8bada05e3e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Serializing.meta b/Assets/FishNet/Runtime/Serializing.meta new file mode 100644 index 0000000..a3b0ae4 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6a3426d8e19d71a4e809a5993e9e6459 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Serializing/AutoPackType.cs b/Assets/FishNet/Runtime/Serializing/AutoPackType.cs new file mode 100644 index 0000000..c51081c --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/AutoPackType.cs @@ -0,0 +1,21 @@ +namespace FishNet.Serializing +{ + /// + /// How to pack data when using serialization. + /// + public enum AutoPackType + { + /// + /// Data will not be compressed. + /// + Unpacked = 0, + /// + /// Data will be compressed to use the least amount of data possible. + /// + Packed = 1, + /// + /// Data will be compressed but not as much as Packed. + /// + PackedLess = 2 + } +} diff --git a/Assets/FishNet/Runtime/Serializing/AutoPackType.cs.meta b/Assets/FishNet/Runtime/Serializing/AutoPackType.cs.meta new file mode 100644 index 0000000..37dd541 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/AutoPackType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2e94ebaa8f7024845a7e90ebd8246ac6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Serializing/Helping.meta b/Assets/FishNet/Runtime/Serializing/Helping.meta new file mode 100644 index 0000000..5ba60eb --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 29ef966943829104e8b9d8b7fd225599 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Serializing/Helping/Attributes.cs b/Assets/FishNet/Runtime/Serializing/Helping/Attributes.cs new file mode 100644 index 0000000..9541eef --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/Attributes.cs @@ -0,0 +1,26 @@ +using FishNet.Utility.Constant; +using System; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo(UtilityConstants.CODEGEN_ASSEMBLY_NAME)] +namespace FishNet.Serializing.Helping +{ + /// + /// Method is a comparer for a value type. + /// + public class CustomComparerAttribute : Attribute { } + /// + /// Method or type will be made public by codegen. + /// + internal class CodegenMakePublicAttribute : Attribute { } + /// + /// Field or type will be excluded from codegen serialization. + /// + public class CodegenExcludeAttribute : Attribute { } + /// + /// THIS DOES NOT DO ANYTHING AT THIS TIME. + /// It would do -> Type will be included in codegen serialization. + /// + internal class CodegenIncludeAttribute : Attribute { } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/Helping/Attributes.cs.meta b/Assets/FishNet/Runtime/Serializing/Helping/Attributes.cs.meta new file mode 100644 index 0000000..72d697d --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/Attributes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ce079c8f32bf87b46a44681ccc8578fa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Serializing/Helping/Broadcasts.cs b/Assets/FishNet/Runtime/Serializing/Helping/Broadcasts.cs new file mode 100644 index 0000000..6086557 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/Broadcasts.cs @@ -0,0 +1,38 @@ +using FishNet.Managing.Scened; +using FishNet.Managing.Server; +using FishNet.Object.Helping; +using FishNet.Transporting; +using UnityEngine; + +namespace FishNet.Serializing.Helping +{ + + internal static class Broadcasts + { + /// + /// Writes a broadcast to writer. + /// + /// + /// + /// + /// + /// + internal static PooledWriter WriteBroadcast(PooledWriter writer, T message, Channel channel) + { + writer.WritePacketId(PacketId.Broadcast); + writer.WriteUInt16(typeof(T).FullName.GetStableHash16()); //muchlater codegen this to pass in hash. use technique similar to rpcs to limit byte/shorts. + //Write data to a new writer. + PooledWriter dataWriter = WriterPool.GetWriter(); + dataWriter.Write(message); + //Write length of data. + writer.WriteLength(dataWriter.Length); + //Write data. + writer.WriteArraySegment(dataWriter.GetArraySegment()); + + dataWriter.Dispose(); + + return writer; + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/Helping/Broadcasts.cs.meta b/Assets/FishNet/Runtime/Serializing/Helping/Broadcasts.cs.meta new file mode 100644 index 0000000..462de30 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/Broadcasts.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0636e29429649a24795091f80edbd892 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Serializing/Helping/Comparers.cs b/Assets/FishNet/Runtime/Serializing/Helping/Comparers.cs new file mode 100644 index 0000000..8f3f2db --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/Comparers.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using UnityEngine.SceneManagement; + +namespace FishNet.Serializing.Helping +{ + + public class GeneratedComparer + { + /// + /// Compare if T is default. + /// + public static Func IsDefault { internal get; set; } + /// + /// Compare if T is the same as T2. + /// + public static Func Compare { internal get; set; } + } + + + public class Comparers + { + /// + /// Returns if A equals B using EqualityCompare. + /// + /// + /// + /// + /// + public static bool EqualityCompare(T a, T b) + { + return EqualityComparer.Default.Equals(a, b); + } + + public static bool IsDefault(T t) + { + return t.Equals(default(T)); + } + + public static bool IsEqualityCompareDefault(T a) + { + return EqualityComparer.Default.Equals(a, default(T)); + } + } + + + internal class SceneComparer : IEqualityComparer + { + public bool Equals(Scene a, Scene b) + { + if (!a.IsValid() || !b.IsValid()) + return false; + + if (a.handle != 0 || b.handle != 0) + return (a.handle == b.handle); + + return (a.name == b.name); + } + + public int GetHashCode(Scene obj) + { + return obj.GetHashCode(); + } + } + +} diff --git a/Assets/FishNet/Runtime/Serializing/Helping/Comparers.cs.meta b/Assets/FishNet/Runtime/Serializing/Helping/Comparers.cs.meta new file mode 100644 index 0000000..c79d11e --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/Comparers.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e912d0645f10b2c458cc2f01e24ecc27 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Serializing/Helping/Quaternion32.cs b/Assets/FishNet/Runtime/Serializing/Helping/Quaternion32.cs new file mode 100644 index 0000000..3fa685a --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/Quaternion32.cs @@ -0,0 +1,163 @@ + +using System; +using UnityEngine; + +namespace FishNet.Serializing.Helping +{ + public static class Quaternion32Compression + { + private const float Maximum = +1.0f / 1.414214f; + + private const int BitsPerAxis = 10; + private const int LargestComponentShift = BitsPerAxis * 3; + private const int AShift = BitsPerAxis * 2; + private const int BShift = BitsPerAxis * 1; + private const int IntScale = (1 << (BitsPerAxis - 1)) - 1; + private const int IntMask = (1 << BitsPerAxis) - 1; + + public static uint Compress(Quaternion quaternion) + { + float absX = Mathf.Abs(quaternion.x); + float absY = Mathf.Abs(quaternion.y); + float absZ = Mathf.Abs(quaternion.z); + float absW = Mathf.Abs(quaternion.w); + + ComponentType largestComponent = ComponentType.X; + float largestAbs = absX; + float largest = quaternion.x; + + if (absY > largestAbs) + { + largestAbs = absY; + largestComponent = ComponentType.Y; + largest = quaternion.y; + } + if (absZ > largestAbs) + { + largestAbs = absZ; + largestComponent = ComponentType.Z; + largest = quaternion.z; + } + if (absW > largestAbs) + { + largestComponent = ComponentType.W; + largest = quaternion.w; + } + + float a = 0; + float b = 0; + float c = 0; + switch (largestComponent) + { + case ComponentType.X: + a = quaternion.y; + b = quaternion.z; + c = quaternion.w; + break; + case ComponentType.Y: + a = quaternion.x; + b = quaternion.z; + c = quaternion.w; + break; + case ComponentType.Z: + a = quaternion.x; + b = quaternion.y; + c = quaternion.w; + break; + case ComponentType.W: + a = quaternion.x; + b = quaternion.y; + c = quaternion.z; + break; + } + + if (largest < 0) + { + a = -a; + b = -b; + c = -c; + } + + uint integerA = ScaleToUint(a); + uint integerB = ScaleToUint(b); + uint integerC = ScaleToUint(c); + + return (((uint)largestComponent) << LargestComponentShift) | (integerA << AShift) | (integerB << BShift) | integerC; + } + + private static uint ScaleToUint(float v) + { + float normalized = v / Maximum; + return (uint)Mathf.RoundToInt(normalized * IntScale) & IntMask; + } + + private static float ScaleToFloat(uint v) + { + float unscaled = v * Maximum / IntScale; + + if (unscaled > Maximum) + unscaled -= Maximum * 2; + return unscaled; + } + + public static Quaternion Decompress(uint compressed) + { + var largestComponentType = (ComponentType)(compressed >> LargestComponentShift); + uint integerA = (compressed >> AShift) & IntMask; + uint integerB = (compressed >> BShift) & IntMask; + uint integerC = compressed & IntMask; + + float a = ScaleToFloat(integerA); + float b = ScaleToFloat(integerB); + float c = ScaleToFloat(integerC); + + Quaternion rotation; + switch (largestComponentType) + { + case ComponentType.X: + // (?) y z w + rotation.y = a; + rotation.z = b; + rotation.w = c; + rotation.x = Mathf.Sqrt(1 - rotation.y * rotation.y + - rotation.z * rotation.z + - rotation.w * rotation.w); + break; + case ComponentType.Y: + // x (?) z w + rotation.x = a; + rotation.z = b; + rotation.w = c; + rotation.y = Mathf.Sqrt(1 - rotation.x * rotation.x + - rotation.z * rotation.z + - rotation.w * rotation.w); + break; + case ComponentType.Z: + // x y (?) w + rotation.x = a; + rotation.y = b; + rotation.w = c; + rotation.z = Mathf.Sqrt(1 - rotation.x * rotation.x + - rotation.y * rotation.y + - rotation.w * rotation.w); + break; + case ComponentType.W: + // x y z (?) + rotation.x = a; + rotation.y = b; + rotation.z = c; + rotation.w = Mathf.Sqrt(1 - rotation.x * rotation.x + - rotation.y * rotation.y + - rotation.z * rotation.z); + break; + default: + // Should never happen! + throw new ArgumentOutOfRangeException("Unknown rotation component type: " + + largestComponentType); + } + + return rotation; + } + + } +} diff --git a/Assets/FishNet/Runtime/Serializing/Helping/Quaternion32.cs.meta b/Assets/FishNet/Runtime/Serializing/Helping/Quaternion32.cs.meta new file mode 100644 index 0000000..0158ec8 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/Quaternion32.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f71e61ed84064a0429577ec462a8fa79 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Serializing/Helping/Quaternion64.cs b/Assets/FishNet/Runtime/Serializing/Helping/Quaternion64.cs new file mode 100644 index 0000000..1832ba7 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/Quaternion64.cs @@ -0,0 +1,192 @@ + +using System; +using UnityEngine; + +namespace FishNet.Serializing.Helping +{ + /// + /// Credit to https://github.com/viliwonka + /// https://github.com/FirstGearGames/FishNet/pull/23 + /// + public static class Quaternion64Compression + { + // 64 bit quaternion compression + // [4 bits] largest component + // [21 bits] higher res + // [21 bits] higher res + // [20 bits] higher res + // sum is 64 bits + private const float Maximum = +1.0f / 1.414214f; + private const int BitsPerAxis_H = 21; // higher res, 21 bits + private const int BitsPerAxis_L = 20; // lower res, 20 bits + private const int LargestComponentShift = BitsPerAxis_H * 2 + BitsPerAxis_L * 1; + private const int AShift = BitsPerAxis_H + BitsPerAxis_L; + private const int BShift = BitsPerAxis_L; + private const int IntScale_H = (1 << (BitsPerAxis_H - 1)) - 1; + private const int IntMask_H = (1 << BitsPerAxis_H) - 1; + private const int IntScale_L = (1 << (BitsPerAxis_L - 1)) - 1; + private const int IntMask_L = (1 << BitsPerAxis_L) - 1; + + public static ulong Compress(Quaternion quaternion) + { + float absX = Mathf.Abs(quaternion.x); + float absY = Mathf.Abs(quaternion.y); + float absZ = Mathf.Abs(quaternion.z); + float absW = Mathf.Abs(quaternion.w); + + ComponentType largestComponent = ComponentType.X; + float largestAbs = absX; + float largest = quaternion.x; + + if (absY > largestAbs) + { + largestAbs = absY; + largestComponent = ComponentType.Y; + largest = quaternion.y; + } + if (absZ > largestAbs) + { + largestAbs = absZ; + largestComponent = ComponentType.Z; + largest = quaternion.z; + } + if (absW > largestAbs) + { + largestComponent = ComponentType.W; + largest = quaternion.w; + } + + float a = 0; + float b = 0; + float c = 0; + + switch (largestComponent) + { + case ComponentType.X: + a = quaternion.y; + b = quaternion.z; + c = quaternion.w; + break; + case ComponentType.Y: + a = quaternion.x; + b = quaternion.z; + c = quaternion.w; + break; + case ComponentType.Z: + a = quaternion.x; + b = quaternion.y; + c = quaternion.w; + break; + case ComponentType.W: + a = quaternion.x; + b = quaternion.y; + c = quaternion.z; + break; + } + + if (largest < 0) + { + a = -a; + b = -b; + c = -c; + } + + ulong integerA = ScaleToUint_H(a); + ulong integerB = ScaleToUint_H(b); + ulong integerC = ScaleToUint_L(c); + + return (((ulong)largestComponent) << LargestComponentShift) | (integerA << AShift) | (integerB << BShift) | integerC; + } + + private static ulong ScaleToUint_H(float v) + { + float normalized = v / Maximum; + return (ulong)Mathf.RoundToInt(normalized * IntScale_H) & IntMask_H; + } + + private static ulong ScaleToUint_L(float v) + { + float normalized = v / Maximum; + return (ulong)Mathf.RoundToInt(normalized * IntScale_L) & IntMask_L; + } + + private static float ScaleToFloat_H(ulong v) + { + float unscaled = v * Maximum / IntScale_H; + + if (unscaled > Maximum) + unscaled -= Maximum * 2; + return unscaled; + } + + private static float ScaleToFloat_L(ulong v) + { + float unscaled = v * Maximum / IntScale_L; + + if (unscaled > Maximum) + unscaled -= Maximum * 2; + return unscaled; + } + + public static Quaternion Decompress(ulong compressed) + { + var largestComponentType = (ComponentType)(compressed >> LargestComponentShift); + ulong integerA = (compressed >> AShift) & IntMask_H; + ulong integerB = (compressed >> BShift) & IntMask_H; + ulong integerC = compressed & IntMask_L; + + float a = ScaleToFloat_H(integerA); + float b = ScaleToFloat_H(integerB); + float c = ScaleToFloat_L(integerC); + + Quaternion rotation; + switch (largestComponentType) + { + case ComponentType.X: + // (?) y z w + rotation.y = a; + rotation.z = b; + rotation.w = c; + rotation.x = Mathf.Sqrt(1 - rotation.y * rotation.y + - rotation.z * rotation.z + - rotation.w * rotation.w); + break; + case ComponentType.Y: + // x (?) z w + rotation.x = a; + rotation.z = b; + rotation.w = c; + rotation.y = Mathf.Sqrt(1 - rotation.x * rotation.x + - rotation.z * rotation.z + - rotation.w * rotation.w); + break; + case ComponentType.Z: + // x y (?) w + rotation.x = a; + rotation.y = b; + rotation.w = c; + rotation.z = Mathf.Sqrt(1 - rotation.x * rotation.x + - rotation.y * rotation.y + - rotation.w * rotation.w); + break; + case ComponentType.W: + // x y z (?) + rotation.x = a; + rotation.y = b; + rotation.z = c; + rotation.w = Mathf.Sqrt(1 - rotation.x * rotation.x + - rotation.y * rotation.y + - rotation.z * rotation.z); + break; + default: + // Should never happen! + throw new ArgumentOutOfRangeException("Unknown rotation component type: " + + largestComponentType); + } + + return rotation; + } + + + } +} diff --git a/Assets/FishNet/Runtime/Serializing/Helping/Quaternion64.cs.meta b/Assets/FishNet/Runtime/Serializing/Helping/Quaternion64.cs.meta new file mode 100644 index 0000000..68090e2 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/Quaternion64.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7afd33d2ca5433f4f831dfaf0169423c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Serializing/Helping/QuaternionConverter.cs b/Assets/FishNet/Runtime/Serializing/Helping/QuaternionConverter.cs new file mode 100644 index 0000000..2855a06 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/QuaternionConverter.cs @@ -0,0 +1,126 @@ + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using UnityEngine; + +namespace FishNet.Serializing.Helping +{ + /// + /// Static class used for fast conversion of quaternion structs. Not thread safe! + /// + [StructLayout(LayoutKind.Explicit)] + public struct QuaternionConverter + { + // [FieldOffset(0)] + // public Quaternion Q; + // [FieldOffset(0)] + // public Quaternion64 Q64; + // [FieldOffset(0)] + // public Quaternion128 Q128; + + // public static QuaternionConverter StaticRef = new QuaternionConverter(); + + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public static Quaternion64 QtoQ64(Quaternion quaternion64) + // { + // StaticRef.Q = quaternion64; + // return StaticRef.Q64; + // } + + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public static Quaternion Q64toQ(Quaternion64 quaternion) + // { + // StaticRef.Q64 = quaternion; + // return StaticRef.Q; + // } + + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public static Quaternion128 QtoQ128(Quaternion quaternion128) + // { + // StaticRef.Q = quaternion128; + // return StaticRef.Q128; + // } + + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public static Quaternion Q128toQ(Quaternion128 quaternion) + // { + // StaticRef.Q128 = quaternion; + // return StaticRef.Q; + // } + //} + + //public struct Quaternion64 + //{ + // public float x; + // public float y; + // public float z; + // public float w; + + // public Quaternion64(float x, float y, float z, float w) + // { + // this.x = x; + // this.y = y; + // this.z = z; + // this.w = w; + // } + + // public Quaternion64(Quaternion q) + // { + // this.x = q.x; + // this.y = q.y; + // this.z = q.z; + // this.w = q.w; + // } + + // /*[MethodImpl(MethodImplOptions.AggressiveInlining)] + // public static implicit operator Quaternion64(Quaternion q) => new Quaternion64(q); + + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public static implicit operator Quaternion(Quaternion64 q) => new Quaternion(q.x, q.y, q.z, q.w);*/ + //} + + //public struct Quaternion128 + //{ + // public float x; + // public float y; + // public float z; + // public float w; + // public Quaternion128(float x, float y, float z, float w) + // { + // this.x = x; + // this.y = y; + // this.z = z; + // this.w = w; + // } + + // public Quaternion128(Quaternion q) + // { + // x = q.x; + // y = q.y; + // z = q.z; + // w = q.w; + // } + + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public static implicit operator Quaternion128(Quaternion q) => new Quaternion128(q); + + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public static implicit operator Quaternion(Quaternion128 q) => new Quaternion(q.x, q.y, q.z, q.w); + //} + + /// + /// Credit to this man for converting gaffer games c code to c# + /// https://gist.github.com/fversnel/0497ad7ab3b81e0dc1dd + /// + } + + public enum ComponentType : uint + { + X = 0, + Y = 1, + Z = 2, + W = 3 + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/Helping/QuaternionConverter.cs.meta b/Assets/FishNet/Runtime/Serializing/Helping/QuaternionConverter.cs.meta new file mode 100644 index 0000000..0238ab7 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/QuaternionConverter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b7ac59ce12259104fa28fc837fb17ccf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Serializing/Helping/ValueConversions.cs b/Assets/FishNet/Runtime/Serializing/Helping/ValueConversions.cs new file mode 100644 index 0000000..48153c7 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/ValueConversions.cs @@ -0,0 +1,41 @@ +using System.Runtime.InteropServices; + +namespace FishNet.Serializing.Helping +{ + + + // -- helpers for float conversion without allocations -- + [StructLayout(LayoutKind.Explicit)] + internal struct UIntFloat + { + [FieldOffset(0)] + public float FloatValue; + + [FieldOffset(0)] + public uint UIntValue; + } + + [StructLayout(LayoutKind.Explicit)] + internal struct UIntDouble + { + [FieldOffset(0)] + public double DoubleValue; + + [FieldOffset(0)] + public ulong LongValue; + } + + [StructLayout(LayoutKind.Explicit)] + internal struct UIntDecimal + { + [FieldOffset(0)] + public ulong LongValue1; + + [FieldOffset(8)] + public ulong LongValue2; + + [FieldOffset(0)] + public decimal DecimalValue; + } + +} diff --git a/Assets/FishNet/Runtime/Serializing/Helping/ValueConversions.cs.meta b/Assets/FishNet/Runtime/Serializing/Helping/ValueConversions.cs.meta new file mode 100644 index 0000000..21b772b --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/ValueConversions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 008e79d0f22a2674189acc7eff64408f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Serializing/Reader.cs b/Assets/FishNet/Runtime/Serializing/Reader.cs new file mode 100644 index 0000000..17e0d50 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Reader.cs @@ -0,0 +1,1421 @@ +using FishNet.Connection; +using FishNet.Documenting; +using FishNet.Managing; +using FishNet.Object; +using FishNet.Object.Prediction; +using FishNet.Serializing.Helping; +using FishNet.Transporting; +using FishNet.Utility.Constant; +using FishNet.Utility.Extension; +using FishNet.Utility.Performance; +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using UnityEngine; + + +[assembly: InternalsVisibleTo(UtilityConstants.GENERATED_ASSEMBLY_NAME)] +//Required for internal tests. +[assembly: InternalsVisibleTo(UtilityConstants.TEST_ASSEMBLY_NAME)] +namespace FishNet.Serializing +{ + /// + /// Used for read references to generic types. + /// + /// + [APIExclude] + public static class GenericReader + { + public static Func Read { internal get; set; } + public static Func ReadAutoPack { internal get; set; } + } + + /// + /// Reads data from a buffer. + /// + public class Reader + { + #region Public. + /// + /// Capacity of the buffer. + /// + public int Capacity => _buffer.Length; + /// + /// NetworkManager for this reader. Used to lookup objects. + /// + public NetworkManager NetworkManager; + /// + /// Offset within the buffer when the reader was created. + /// + public int Offset { get; private set; } + /// + /// Position for the next read. + /// + public int Position; + /// + /// Total number of bytes available within the buffer. + /// + public int Length { get; private set; } + /// + /// Bytes remaining to be read. This value is Length - Position. + /// + public int Remaining => ((Length + Offset) - Position); + #endregion + + #region Internal. + /// + /// NetworkConnection that this data came from. + /// Value may not always be set. + /// + public NetworkConnection NetworkConnection { get; private set; } +#if UNITY_EDITOR || DEVELOPMENT_BUILD + /// + /// Last NetworkObject parsed. + /// + public static NetworkObject LastNetworkObject { get; private set; } + /// + /// Last NetworkBehaviour parsed. + /// + public static NetworkBehaviour LastNetworkBehaviour { get; private set; } +#endif + #endregion + + #region Private. + /// + /// Data being read. + /// + private byte[] _buffer; + #endregion + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Reader(byte[] bytes, NetworkManager networkManager, NetworkConnection networkConnection = null) + { + Initialize(bytes, networkManager, networkConnection); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Reader(ArraySegment segment, NetworkManager networkManager, NetworkConnection networkConnection = null) + { + Initialize(segment, networkManager, networkConnection); + } + + /// + /// Outputs reader to string. + /// + /// + public override string ToString() + { + return $"Position: {Position}, Length: {Length}, Buffer: {BitConverter.ToString(_buffer, Offset, Length)}."; + } + + /// + /// Initializes this reader with data. + /// + /// + /// + internal void Initialize(ArraySegment bytes, NetworkManager networkManager, NetworkConnection networkConnection = null) + { + if (bytes.Array == null) + { + if (_buffer == null) + _buffer = new byte[0]; + } + else + { + _buffer = bytes.Array; + } + + Position = bytes.Offset; + Offset = bytes.Offset; + Length = bytes.Count; + NetworkManager = networkManager; + NetworkConnection = networkConnection; + } + /// + /// Initializes this reader with data. + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void Initialize(byte[] bytes, NetworkManager networkManager, NetworkConnection networkConnection = null) + { + Initialize(new ArraySegment(bytes), networkManager, networkConnection); + } + + + /// + /// Reads a dictionary. + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [Obsolete("Use ReadDictionaryAllocated.")] + public Dictionary ReadDictionary() + { + return ReadDictionaryAllocated(); + } + + /// + /// Reads a dictionary. + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Dictionary ReadDictionaryAllocated() + { + bool isNull = ReadBoolean(); + if (isNull) + return null; + + int count = ReadInt32(); + + Dictionary result = new Dictionary(count); + for (int i = 0; i < count; i++) + { + TKey key = Read(); + TValue value = Read(); + result.Add(key, value); + } + + return result; + } + + + /// + /// Reads length. This method is used to make debugging easier. + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal int ReadLength() + { + return ReadInt32(); + } + + /// + /// Reads a packetId. + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal PacketId ReadPacketId() + { + return (PacketId)ReadUInt16(); + } + + /// + /// Returns a ushort without advancing the reader. + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal PacketId PeekPacketId() + { + int currentPosition = Position; + PacketId result = ReadPacketId(); + Position = currentPosition; + return result; + } + + /// + /// Skips a number of bytes in the reader. + /// + /// Number of bytes to skip. + [CodegenExclude] + public void Skip(int value) + { + if (value < 1 || Remaining < value) + return; + + Position += value; + } + /// + /// Clears remaining bytes to be read. + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + if (Remaining > 0) + Skip(Remaining); + } + + /// + /// Returns the buffer as an ArraySegment. + /// + /// + public ArraySegment GetArraySegmentBuffer() + { + return new ArraySegment(_buffer, Offset, Length); + } + /// + /// Returns the buffer as bytes. This does not trim excessive bytes. + /// + /// + public byte[] GetByteBuffer() + { + return _buffer; + } + /// + /// Returns the buffer as bytes and allocates into a new array. + /// + /// + public byte[] GetByteBufferAllocated() + { + byte[] result = new byte[Length]; + Buffer.BlockCopy(_buffer, Offset, result, 0, Length); + return result; + } + /// + /// BlockCopies data from the reader to target and advances reader. + /// + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void BlockCopy(ref byte[] target, int targetOffset, int count) + { + Buffer.BlockCopy(_buffer, Position, target, targetOffset, count); + Position += count; + } + + /// + /// Reads a byte. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte ReadByte() + { + byte r = _buffer[Position]; + Position += 1; + return r; + } + + /// + /// Read bytes from position into target. + /// + /// Buffer to read bytes into. + /// Number of bytes to read. + [CodegenExclude] + public void ReadBytes(ref byte[] buffer, int count) + { + if (buffer == null) + throw new EndOfStreamException($"Target is null."); + //Target isn't large enough. + if (count > buffer.Length) + throw new EndOfStreamException($"Count of {count} exceeds target length of {buffer.Length}."); + + BlockCopy(ref buffer, 0, count); + } + + /// + /// Creates an ArraySegment by reading a number of bytes from position. + /// + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ArraySegment ReadArraySegment(int count) + { + ArraySegment result = new ArraySegment(_buffer, Position, count); + Position += count; + return result; + } + + /// + /// Reads a sbyte. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public sbyte ReadSByte() + { + return (sbyte)ReadByte(); + } + + /// + /// Reads a char. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public char ReadChar() => (char)ReadUInt16(); + + /// + /// Reads a boolean. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool ReadBoolean() + { + byte result = ReadByte(); + return (result == 1) ? true : false; + } + + /// + /// Reads an int16. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ushort ReadUInt16() + { + ushort result = 0; + result |= _buffer[Position++]; + result |= (ushort)(_buffer[Position++] << 8); + + return result; + } + + /// + /// Reads a uint16. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public short ReadInt16() => (short)ReadUInt16(); + + /// + /// Reads an int32. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public uint ReadUInt32(AutoPackType packType = AutoPackType.Packed) + { + if (packType == AutoPackType.Packed) + return (uint)ReadPackedWhole(); + + uint result = 0; + result |= _buffer[Position++]; + result |= (uint)_buffer[Position++] << 8; + result |= (uint)_buffer[Position++] << 16; + result |= (uint)_buffer[Position++] << 24; + + return result; + } + /// + /// Reads a uint32. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ReadInt32(AutoPackType packType = AutoPackType.Packed) + { + if (packType == AutoPackType.Packed) + return (int)(long)ZigZagDecode(ReadPackedWhole()); + + return (int)ReadUInt32(packType); + } + + /// + /// Reads a uint64. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long ReadInt64(AutoPackType packType = AutoPackType.Packed) + { + if (packType == AutoPackType.Packed) + return (long)ZigZagDecode(ReadPackedWhole()); + + return (long)ReadUInt64(packType); + } + + /// + /// Reads an int64. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ulong ReadUInt64(AutoPackType packType = AutoPackType.Packed) + { + if (packType == AutoPackType.Packed) + return (ulong)ReadPackedWhole(); + + ulong result = 0; + result |= _buffer[Position++]; + result |= (ulong)_buffer[Position++] << 8; + result |= (ulong)_buffer[Position++] << 16; + result |= (ulong)_buffer[Position++] << 24; + result |= (ulong)_buffer[Position++] << 32; + result |= (ulong)_buffer[Position++] << 40; + result |= (ulong)_buffer[Position++] << 48; + result |= (ulong)_buffer[Position++] << 56; + + return result; + } + + + /// + /// Reads a single. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float ReadSingle(AutoPackType packType = AutoPackType.Unpacked) + { + if (packType == AutoPackType.Unpacked) + { + UIntFloat converter = new UIntFloat(); + converter.UIntValue = ReadUInt32(AutoPackType.Unpacked); + return converter.FloatValue; + } + else + { + long converter = (long)ReadPackedWhole(); + return (float)(converter / 100f); + } + } + + /// + /// Reads a double. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double ReadDouble() + { + UIntDouble converter = new UIntDouble(); + converter.LongValue = ReadUInt64(AutoPackType.Unpacked); + return converter.DoubleValue; + } + + /// + /// Reads a decimal. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public decimal ReadDecimal() + { + UIntDecimal converter = new UIntDecimal(); + converter.LongValue1 = ReadUInt64(AutoPackType.Unpacked); + converter.LongValue2 = ReadUInt64(AutoPackType.Unpacked); + return converter.DecimalValue; + } + + /// + /// Reads a string. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public string ReadString() + { + int size = ReadInt32(); + //Null string. + if (size == Writer.UNSET_COLLECTION_SIZE_VALUE) + return null; + else if (size == 0) + return string.Empty; + + if (!CheckAllocationAttack(size)) + return string.Empty; + ArraySegment data = ReadArraySegment(size); + return ReaderStatics.GetString(data); + } + + /// + /// Creates a byte array and reads bytes and size into it. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte[] ReadBytesAndSizeAllocated() + { + int size = ReadInt32(); + if (size == Writer.UNSET_COLLECTION_SIZE_VALUE) + return null; + else + return ReadBytesAllocated(size); + } + + /// + /// Reads bytes and size and copies results into target. Returns UNSET if null was written. + /// + /// Bytes read. + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ReadBytesAndSize(ref byte[] target) + { + int size = ReadInt32(); + if (size > 0) + ReadBytes(ref target, size); + + return size; + } + + /// + /// Reads bytes and size and returns as an ArraySegment. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ArraySegment ReadArraySegmentAndSize() + { + int size = ReadInt32(); + /* UNSET would be written for null. But since + * ArraySegments cannot be null return default if + * length is unset or 0. */ + if (size == Writer.UNSET_COLLECTION_SIZE_VALUE || size == 0) + return default; + + return ReadArraySegment(size); + } + + /// + /// Reads a Vector2. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2 ReadVector2() + { + return new Vector2(ReadSingle(), ReadSingle()); + } + + /// + /// Reads a Vector3. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 ReadVector3() + { + return new Vector3(ReadSingle(), ReadSingle(), ReadSingle()); + } + + /// + /// Reads a Vector4. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ReadVector4() + { + return new Vector4(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle()); + } + + /// + /// Reads a Vector2Int. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2Int ReadVector2Int(AutoPackType packType = AutoPackType.Packed) + { + return new Vector2Int(ReadInt32(packType), ReadInt32(packType)); + } + + /// + /// Reads a Vector3Int. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3Int ReadVector3Int(AutoPackType packType = AutoPackType.Packed) + { + return new Vector3Int(ReadInt32(packType), ReadInt32(packType), ReadInt32(packType)); + } + + /// + /// Reads a color. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Color ReadColor(AutoPackType packType = AutoPackType.Packed) + { + float r, g, b, a; + if (packType == AutoPackType.Unpacked) + { + r = ReadSingle(); + g = ReadSingle(); + b = ReadSingle(); + a = ReadSingle(); + } + else + { + r = (float)(ReadByte() / 100f); + g = (float)(ReadByte() / 100f); + b = (float)(ReadByte() / 100f); + a = (float)(ReadByte() / 100f); + } + return new Color(r, g, b, a); + } + + /// + /// Reads a Color32. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Color32 ReadColor32() + { + return new Color32(ReadByte(), ReadByte(), ReadByte(), ReadByte()); + } + + /// + /// Reads a Quaternion. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Quaternion ReadQuaternion(AutoPackType packType = AutoPackType.Packed) + { + if (packType == AutoPackType.Packed) + { + uint result = ReadUInt32(AutoPackType.Unpacked); + return Quaternion32Compression.Decompress(result); + } + else if (packType == AutoPackType.PackedLess) + { + ulong result = ReadUInt64(AutoPackType.Unpacked); + return Quaternion64Compression.Decompress(result); + } + else + { + return new Quaternion( + ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle() + ); + } + } + + /// + /// Reads a Rect. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rect ReadRect() + { + return new Rect(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle()); + } + + /// + /// Plane. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Plane ReadPlane() + { + return new Plane(ReadVector3(), ReadSingle()); + } + + /// + /// Reads a Ray. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Ray ReadRay() + { + Vector3 position = ReadVector3(); + Vector3 direction = ReadVector3(); + return new Ray(position, direction); + } + + /// + /// Reads a Ray. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Ray2D ReadRay2D() + { + Vector3 position = ReadVector2(); + Vector2 direction = ReadVector2(); + return new Ray2D(position, direction); + } + + /// + /// Reads a Matrix4x4. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Matrix4x4 ReadMatrix4x4() + { + Matrix4x4 result = new Matrix4x4 + { + m00 = ReadSingle(), + m01 = ReadSingle(), + m02 = ReadSingle(), + m03 = ReadSingle(), + m10 = ReadSingle(), + m11 = ReadSingle(), + m12 = ReadSingle(), + m13 = ReadSingle(), + m20 = ReadSingle(), + m21 = ReadSingle(), + m22 = ReadSingle(), + m23 = ReadSingle(), + m30 = ReadSingle(), + m31 = ReadSingle(), + m32 = ReadSingle(), + m33 = ReadSingle() + }; + + return result; + } + + /// + /// Creates a new byte array and reads bytes into it. + /// + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte[] ReadBytesAllocated(int count) + { + byte[] bytes = new byte[count]; + ReadBytes(ref bytes, count); + return bytes; + } + + /// + /// Reads a Guid. + /// + /// + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public System.Guid ReadGuid() + { + byte[] buffer = ReaderStatics.GetGuidBuffer(); + ReadBytes(ref buffer, 16); + return new System.Guid(buffer); + } + + + /// + /// Reads a GameObject. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public GameObject ReadGameObject() + { + NetworkObject nob = ReadNetworkObject(); + return (nob == null) ? null : nob.gameObject; + } + + + /// + /// Reads a Transform. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Transform ReadTransform() + { + NetworkObject nob = ReadNetworkObject(); + return (nob == null) ? null : nob.transform; + } + + + /// + /// Reads a NetworkObject. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public NetworkObject ReadNetworkObject() + { + return ReadNetworkObject(out _); + } + + /// + /// Reads a NetworkObject. + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public NetworkObject ReadNetworkObject(out int objectOrPrefabId) + { +#if UNITY_EDITOR || DEVELOPMENT_BUILD + LastNetworkBehaviour = null; +#endif + objectOrPrefabId = ReadNetworkObjectId(); + bool isSpawned; + /* UNSET indicates that the object + * is null or no PrefabId is set. + * PrefabIds are set in Awake within + * the NetworkManager so that should + * never happen so long as nob isn't null. */ + if (objectOrPrefabId == NetworkObject.UNSET_OBJECTID_VALUE) + return null; + else + isSpawned = ReadBoolean(); + + bool isServer = NetworkManager.ServerManager.Started; + bool isClient = NetworkManager.ClientManager.Started; + + NetworkObject result; + //Is spawned. + if (isSpawned) + { + result = null; + /* Try to get the object client side first if client + * is running. When acting as a host generally the object + * will be available in the server and client list + * but there can be occasions where the server side + * deinitializes the object, making it unavailable, while + * it is still available in the client side. Since FishNet doesn't + * use a fake host connection like some lesser solutions the client + * has to always be treated as it's own entity. */ + if (isClient) + NetworkManager.ClientManager.Objects.Spawned.TryGetValueIL2CPP(objectOrPrefabId, out result); + //If not found on client and server is running then try server. + if (result == null && isServer) + NetworkManager.ServerManager.Objects.Spawned.TryGetValueIL2CPP(objectOrPrefabId, out result); + } + //Not spawned. + else + { + //Only look up asServer if not client, otherwise use client. + bool asServer = !isClient; + //Look up prefab. + result = NetworkManager.GetPrefab(objectOrPrefabId, asServer); + } + +#if UNITY_EDITOR || DEVELOPMENT_BUILD + LastNetworkObject = result; +#endif + return result; + } + + /// + /// Reads a NetworkObjectId and nothing else. + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ReadNetworkObjectId() + { + return ReadUInt16(); + } + + /// + /// Reads the Id for a NetworkObject and outputs spawn settings. + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal int ReadNetworkObjectForSpawn(out sbyte initializeOrder, out ushort collectionid, out bool spawned) + { + int objectId = ReadNetworkObjectId(); + + bool isNull = (objectId == NetworkObject.UNSET_OBJECTID_VALUE); + if (isNull) + { + initializeOrder = 0; + collectionid = 0; + spawned = false; + } + else + { + collectionid = ReadUInt16(); + initializeOrder = ReadSByte(); + spawned = ReadBoolean(); + } + + return objectId; + } + + + /// + /// Reads the Id for a NetworkObject and outputs despawn settings. + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal int ReadNetworkObjectForDepawn(out DespawnType dt) + { + int objectId = ReadNetworkObjectId(); + dt = (DespawnType)ReadByte(); + return objectId; + } + + + /// + /// Reads a NetworkBehaviourId and ObjectId. + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal byte ReadNetworkBehaviourId(out int objectId) + { + objectId = ReadNetworkObjectId(); + if (objectId != NetworkObject.UNSET_OBJECTID_VALUE) + return ReadByte(); + else + return 0; + } + + /// + /// Reads a NetworkBehaviour. + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public NetworkBehaviour ReadNetworkBehaviour(out int objectId, out byte componentIndex) + { + NetworkObject nob = ReadNetworkObject(out objectId); + componentIndex = ReadByte(); + + NetworkBehaviour result; + if (nob == null) + { + result = null; + } + else + { + if (componentIndex >= nob.NetworkBehaviours.Length) + { + NetworkManager.LogError($"ComponentIndex of {componentIndex} is out of bounds on {nob.gameObject.name} [id {nob.ObjectId}]. This may occur if you have modified your gameObject/prefab without saving it, or the scene."); + result = null; + } + else + { + result = nob.NetworkBehaviours[componentIndex]; + } + } + +#if UNITY_EDITOR || DEVELOPMENT_BUILD + LastNetworkBehaviour = result; +#endif + return result; + } + + /// + /// Reads a NetworkBehaviour. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public NetworkBehaviour ReadNetworkBehaviour() + { + return ReadNetworkBehaviour(out _, out _); + } + + /// + /// Reads a DateTime. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public DateTime ReadDateTime() + { + DateTime result = DateTime.FromBinary(ReadInt64()); + return result; + } + + + /// + /// Reads a transport channel. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Channel ReadChannel() + { + return (Channel)ReadByte(); + } + + /// + /// Reads the Id for a NetworkConnection. + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ReadNetworkConnectionId() + { + return ReadInt16(); + } + + /// + /// Writes a NetworkConnection. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public NetworkConnection ReadNetworkConnection() + { + int value = ReadNetworkConnectionId(); + if (value == NetworkConnection.UNSET_CLIENTID_VALUE) + { + return FishNet.Managing.NetworkManager.EmptyConnection; + } + else + { + //Prefer server. + if (NetworkManager.IsServer) + { + NetworkConnection result; + if (NetworkManager.ServerManager.Clients.TryGetValueIL2CPP(value, out result)) + { + return result; + } + //If also client then try client side data. + else if (NetworkManager.IsClient) + { + //If found in client collection then return. + if (NetworkManager.ClientManager.Clients.TryGetValueIL2CPP(value, out result)) + return result; + /* Otherwise make a new instance. + * We do not know if this is for the server or client so + * initialize it either way. Connections rarely come through + * without being in server/client side collection. */ + else + return new NetworkConnection(NetworkManager, value, true); + + } + //Only server and not found. + else + { + NetworkManager.LogWarning($"Unable to find connection for read Id {value}. An empty connection will be returned."); + return FishNet.Managing.NetworkManager.EmptyConnection; + } + } + //Try client side, will only be able to fetch against local connection. + else + { + //If value is self then return self. + if (value == NetworkManager.ClientManager.Connection.ClientId) + return NetworkManager.ClientManager.Connection; + //Try client side dictionary. + else if (NetworkManager.ClientManager.Clients.TryGetValueIL2CPP(value, out NetworkConnection result)) + return result; + /* Otherwise make a new instance. + * We do not know if this is for the server or client so + * initialize it either way. Connections rarely come through + * without being in server/client side collection. */ + else + return new NetworkConnection(NetworkManager, value, true); + } + + } + } + + /// + /// Checks if the size could possibly be an allocation attack. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool CheckAllocationAttack(int size) + { + /* Possible attacks. Impossible size, or size indicates + * more elements in collection or more bytes needed + * than what bytes are available. */ + if (size != Writer.UNSET_COLLECTION_SIZE_VALUE && size < 0) + { + NetworkManager.LogError($"Size of {size} is invalid."); + return false; + } + if (size > Remaining) + { + NetworkManager.LogError($"Read size of {size} is larger than remaining data of {Remaining}."); + return false; + } + + //Checks pass. + return true; + } + + + #region Packed readers. + /// + /// ZigZag decode an integer. Move the sign bit back to the left. + /// + public ulong ZigZagDecode(ulong value) + { + ulong sign = value << 63; + if (sign > 0) + return ~(value >> 1) | sign; + return value >> 1; + } + /// + /// Reads a packed whole number. + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ulong ReadPackedWhole() + { + byte data = ReadByte(); + ulong result = (ulong)(data & 0x7F); + if ((data & 0x80) == 0) return result; + + data = ReadByte(); + result |= (ulong)(data & 0x7F) << 7; + if ((data & 0x80) == 0) return result; + + data = ReadByte(); + result |= (ulong)(data & 0x7F) << 14; + if ((data & 0x80) == 0) return result; + + data = ReadByte(); + result |= (ulong)(data & 0x7F) << 21; + if ((data & 0x80) == 0) return result; + + data = ReadByte(); + result |= (ulong)(data & 0x0F) << 28; + int extraBytes = data >> 4; + + switch (extraBytes) + { + case 0: + break; + case 1: + result |= (ulong)ReadByte() << 32; + break; + case 2: + result |= (ulong)ReadByte() << 32; + result |= (ulong)ReadByte() << 40; + break; + case 3: + result |= (ulong)ReadByte() << 32; + result |= (ulong)ReadByte() << 40; + result |= (ulong)ReadByte() << 48; + break; + case 4: + result |= (ulong)ReadByte() << 32; + result |= (ulong)ReadByte() << 40; + result |= (ulong)ReadByte() << 48; + result |= (ulong)ReadByte() << 56; + break; + } + return result; + } + #endregion + + #region Generators. + /// + /// Reads a replicate into collection and returns item count read. + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal int ReadReplicate(ref T[] collection, uint tick) where T : IReplicateData + { + //Number of entries written. + int count = (int)ReadByte(); + if (collection == null || collection.Length < count) + collection = new T[count]; + + /* Subtract count total minus 1 + * from starting tick. This sets the tick to what the first entry would be. + * EG packet came in as tick 100, so that was passed as tick. + * if there are 3 replicates then 2 would be subtracted (count - 1). + * The new tick would be 98. + * Ticks would be assigned to read values from oldest to + * newest as 98, 99, 100. Which is the correct result. In order for this to + * work properly past replicates cannot skip ticks. This will be ensured + * in another part of the code. */ + tick -= (uint)(count - 1); + + int fullPackType = ReadByte(); + //Read once and apply to all entries. + if (fullPackType > 0) + { + T value; + if (fullPackType == Writer.REPLICATE_ALL_DEFAULT_BYTE) + { + value = default(T); + } + else if (fullPackType == Writer.REPLICATE_REPEATING_BYTE) + { + value = Read(); + } + else + { + value = default(T); + NetworkManager?.LogError($"Unhandled Replicate pack type {fullPackType}."); + } + + for (int i = 0; i < count; i++) + { + collection[i] = value; + collection[i].SetTick(tick + (uint)i); + } + } + //Values vary, read each indicator. + else + { + T lastData = default; + + for (int i = 0; i < count; i++) + { + T value = default; + byte indicatorB = ReadByte(); + if (indicatorB == Writer.REPLICATE_DUPLICATE_BYTE) + { + value = lastData; + } + else if (indicatorB == Writer.REPLICATE_UNIQUE_BYTE) + { + value = Read(); + lastData = value; + } + else if (indicatorB == Writer.REPLICATE_DEFAULT_BYTE) + { + value = default(T); + } + + //Apply tick. + value.SetTick(tick + (uint)i); + //Assign to collection. + collection[i] = value; + } + } + + return count; + } + + /// + /// Reads a ListCache with allocations. + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ListCache ReadListCacheAllocated() + { + List lst = ReadListAllocated(); + ListCache lc = new ListCache(); + lc.Collection = lst; + return lc; + } + /// + /// Reads a ListCache and returns the item count read. + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ReadListCache(ref ListCache listCache) + { + listCache.Collection = ReadListAllocated(); + return listCache.Collection.Count; + } + /// + /// Reads a list with allocations. + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public List ReadListAllocated() + { + List result = null; + ReadList(ref result); + return result; + } + + /// + /// Reads into collection and returns item count read. + /// + /// + /// True to allow the referenced collection to be nullified when receiving a null collection read. + /// Number of values read into the collection. UNSET is returned if the collection were read as null. + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ReadList(ref List collection, bool allowNullification = false) + { + int count = ReadInt32(); + if (count == Writer.UNSET_COLLECTION_SIZE_VALUE) + { + if (allowNullification) + collection = null; + return Writer.UNSET_COLLECTION_SIZE_VALUE; + } + else + { + if (collection == null) + collection = new List(count); + else + collection.Clear(); + + + for (int i = 0; i < count; i++) + collection.Add(Read()); + + return count; + } + } + /// + /// Reads an array. + /// + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T[] ReadArrayAllocated() + { + T[] result = null; + ReadArray(ref result); + return result; + } + /// + /// Reads into collection and returns amount read. + /// + /// + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ReadArray(ref T[] collection) + { + int count = ReadInt32(); + if (count == Writer.UNSET_COLLECTION_SIZE_VALUE) + { + return 0; + } + else if (count == 0) + { + if (collection == null) + collection = new T[0]; + + return 0; + } + else + { + //Initialize buffer if not already done. + if (collection == null) + collection = new T[count]; + else if (collection.Length < count) + Array.Resize(ref collection, count); + + for (int i = 0; i < count; i++) + collection[i] = Read(); + + return count; + } + } + + /// + /// Reads any supported type. + /// + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Read() + { + System.Type type = typeof(T); + if (IsAutoPackType(type, out AutoPackType packType)) + { + Func autopackDel = GenericReader.ReadAutoPack; + if (autopackDel == null) + { + LogError(GetLogMessage()); + return default; + } + else + { + return autopackDel.Invoke(this, packType); + } + } + else + { + Func del = GenericReader.Read; + if (del == null) + { + LogError(GetLogMessage()); + return default; + } + else + { + return del.Invoke(this); + } + } + + string GetLogMessage() => $"Read method not found for {type.FullName}. Use a supported type or create a custom serializer."; + } + + /// + /// Logs an error. + /// + /// + private void LogError(string msg) + { + if (NetworkManager == null) + NetworkManager.StaticLogError(msg); + else + NetworkManager.LogError(msg); + } + + /// + /// Returns if T takes AutoPackType argument. + /// + /// Outputs the default pack type for T. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal bool IsAutoPackType(out AutoPackType packType) => Writer.IsAutoPackType(out packType); + /// + /// Returns if T takes AutoPackType argument. + /// + /// Outputs the default pack type for T. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal bool IsAutoPackType(Type type, out AutoPackType packType) => Writer.IsAutoPackType(type, out packType); + #endregion + } +} diff --git a/Assets/FishNet/Runtime/Serializing/Reader.cs.meta b/Assets/FishNet/Runtime/Serializing/Reader.cs.meta new file mode 100644 index 0000000..228c99c --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Reader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 680939c6cee93b64ba149da2029f4308 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Serializing/ReaderExtensions.cs b/Assets/FishNet/Runtime/Serializing/ReaderExtensions.cs new file mode 100644 index 0000000..5fc5767 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/ReaderExtensions.cs @@ -0,0 +1,18 @@ +using FishNet.Connection; +using FishNet.Documenting; +using FishNet.Object; +using FishNet.Serializing.Helping; +using FishNet.Transporting; +using System; +using UnityEngine; + +namespace FishNet.Serializing +{ + /// + /// Extensions to Read methods. Used by Read. + /// + [APIExclude] + public static class ReaderExtensions + { + } +} diff --git a/Assets/FishNet/Runtime/Serializing/ReaderExtensions.cs.meta b/Assets/FishNet/Runtime/Serializing/ReaderExtensions.cs.meta new file mode 100644 index 0000000..1319c52 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/ReaderExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: abcc77fe436138b4082ee27da3055bb3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Serializing/ReaderPool.cs b/Assets/FishNet/Runtime/Serializing/ReaderPool.cs new file mode 100644 index 0000000..da918c0 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/ReaderPool.cs @@ -0,0 +1,68 @@ +using FishNet.Managing; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace FishNet.Serializing +{ + /// + /// Reader which is reused to save on garbage collection and performance. + /// + public sealed class PooledReader : Reader, IDisposable + { + internal PooledReader(byte[] bytes, NetworkManager networkManager) : base(bytes, networkManager) { } + internal PooledReader(ArraySegment segment, NetworkManager networkManager) : base(segment, networkManager) { } + public void Dispose() => ReaderPool.Recycle(this); + } + + /// + /// Collection of PooledReader. Stores and gets PooledReader. + /// + public static class ReaderPool + { + #region Private. + /// + /// Pool of readers. + /// + private static readonly Stack _pool = new Stack(); + #endregion + + /// + /// Get the next reader in the pool + /// If pool is empty, creates a new Reader + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PooledReader GetReader(byte[] bytes, NetworkManager networkManager) + { + return GetReader(new ArraySegment(bytes), networkManager); + } + + /// + /// Get the next reader in the pool or creates a new one if none are available. + /// + public static PooledReader GetReader(ArraySegment segment, NetworkManager networkManager) + { + PooledReader result; + if (_pool.Count > 0) + { + result = _pool.Pop(); + result.Initialize(segment, networkManager); + } + else + { + result = new PooledReader(segment, networkManager); + } + + return result; + } + + /// + /// Puts reader back into pool + /// When pool is full, the extra reader is left for the GC + /// + public static void Recycle(PooledReader reader) + { + _pool.Push(reader); + } + } +} diff --git a/Assets/FishNet/Runtime/Serializing/ReaderPool.cs.meta b/Assets/FishNet/Runtime/Serializing/ReaderPool.cs.meta new file mode 100644 index 0000000..eb0bdae --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/ReaderPool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 318b117dd2ebd1b4b9e2021796b45eee +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Serializing/ReaderStatics.cs b/Assets/FishNet/Runtime/Serializing/ReaderStatics.cs new file mode 100644 index 0000000..d99bec2 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/ReaderStatics.cs @@ -0,0 +1,47 @@ +using FishNet.Documenting; +using System; +using System.Text; + +namespace FishNet.Serializing +{ + + /// + /// Writes data to a buffer. + /// + [APIExclude] + internal class ReaderStatics + { + /* Since serializing occurs on the main thread this value may + * be shared among all readers. //multithread + */ + + #region Private. + /// + /// Buffer to copy Guids into. + /// + private static byte[] _guidBuffer = new byte[16]; + /// + /// Used to encode strings. + /// + private static readonly UTF8Encoding _encoding = new UTF8Encoding(false, true); + #endregion + + /// + /// Gets the GUID Buffer. + /// + /// + public static byte[] GetGuidBuffer() + { + return _guidBuffer; + } + + /// + /// Returns a string from data. + /// + public static string GetString(ArraySegment data) + { + return _encoding.GetString(data.Array, data.Offset, data.Count); + } + + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/ReaderStatics.cs.meta b/Assets/FishNet/Runtime/Serializing/ReaderStatics.cs.meta new file mode 100644 index 0000000..9c2ea4a --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/ReaderStatics.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c1f98beaf8f697d4b8bb1e6b6ef32d42 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Serializing/SceneComparer.cs b/Assets/FishNet/Runtime/Serializing/SceneComparer.cs new file mode 100644 index 0000000..0e4c687 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/SceneComparer.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using UnityEngine.SceneManagement; + +namespace FishNet.Serializing.Helping +{ + internal sealed class SceneHandleEqualityComparer : EqualityComparer + { + public override bool Equals(Scene a, Scene b) + { + return (a.handle == b.handle); + } + + public override int GetHashCode(Scene obj) + { + return obj.handle; + } + } +} diff --git a/Assets/FishNet/Runtime/Serializing/SceneComparer.cs.meta b/Assets/FishNet/Runtime/Serializing/SceneComparer.cs.meta new file mode 100644 index 0000000..0e09f91 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/SceneComparer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 161dbbe3995ff53479fc4e259f86549d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Serializing/TransformPackingData.cs b/Assets/FishNet/Runtime/Serializing/TransformPackingData.cs new file mode 100644 index 0000000..4d1d8d9 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/TransformPackingData.cs @@ -0,0 +1,10 @@ +namespace FishNet.Serializing +{ + [System.Serializable] + internal class TransformPackingData + { + public AutoPackType Position = AutoPackType.Packed; + public AutoPackType Rotation = AutoPackType.Packed; + public AutoPackType Scale = AutoPackType.Packed; + } +} diff --git a/Assets/FishNet/Runtime/Serializing/TransformPackingData.cs.meta b/Assets/FishNet/Runtime/Serializing/TransformPackingData.cs.meta new file mode 100644 index 0000000..2c3013a --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/TransformPackingData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 80a80dabe02daf6428cce0f16ea49877 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Serializing/Writer.cs b/Assets/FishNet/Runtime/Serializing/Writer.cs new file mode 100644 index 0000000..a7dd6c7 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Writer.cs @@ -0,0 +1,1331 @@ +using FishNet.Connection; +using FishNet.Documenting; +using FishNet.Managing; +using FishNet.Object; +using FishNet.Serializing.Helping; +using FishNet.Transporting; +using FishNet.Utility.Constant; +using FishNet.Utility.Performance; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +[assembly: InternalsVisibleTo(UtilityConstants.GENERATED_ASSEMBLY_NAME)] +namespace FishNet.Serializing +{ + + /// + /// Used for write references to generic types. + /// + /// + [APIExclude] + public static class GenericWriter + { + public static Action Write { get; set; } + public static Action WriteAutoPack { get; set; } + } + + /// + /// Writes data to a buffer. + /// + public class Writer + { + #region Public. + /// + /// Capacity of the buffer. + /// + public int Capacity => _buffer.Length; + /// + /// Current write position. + /// + public int Position; + /// + /// Number of bytes writen to the buffer. + /// + public int Length; + /// + /// NetworkManager associated with this writer. May be null. + /// + public NetworkManager NetworkManager; + #endregion + + #region Private. + /// + /// Buffer to prevent new allocations. This will grow as needed. + /// + private byte[] _buffer = new byte[64]; + #endregion + + #region Const. + /// + /// Replicate data is default of T. + /// + internal const byte REPLICATE_DEFAULT_BYTE = 0; + /// + /// Replicate data is the same as the previous. + /// + internal const byte REPLICATE_DUPLICATE_BYTE = 1; + /// + /// Replicate data is different from the previous. + /// + internal const byte REPLICATE_UNIQUE_BYTE = 2; + /// + /// Replicate data is repeating for every entry. + /// + internal const byte REPLICATE_REPEATING_BYTE = 3; + /// + /// All datas in the replicate are default. + /// + internal const byte REPLICATE_ALL_DEFAULT_BYTE = 4; + /// + /// Value used when a collection is unset, as in null. + /// + public const int UNSET_COLLECTION_SIZE_VALUE = -1; + #endregion + + /// + /// Resets the writer as though it was unused. Does not reset buffers. + /// + public void Reset(NetworkManager manager = null) + { + Length = 0; + Position = 0; + NetworkManager = manager; + } + + /// + /// Writes a dictionary. + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteDictionary(Dictionary dict) + { + if (dict == null) + { + WriteBoolean(true); + return; + } + else + { + WriteBoolean(false); + } + + WriteInt32(dict.Count); + foreach (KeyValuePair item in dict) + { + Write(item.Key); + Write(item.Value); + } + } + + /// + /// Ensures the buffer Capacity is of minimum count. + /// + /// + public void EnsureBufferCapacity(int count) + { + if (Capacity < count) + Array.Resize(ref _buffer, count); + } + + /// + /// Ensure a number of bytes to be available in the buffer from current position. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void EnsureBufferLength(int count) + { + if (Position + count > _buffer.Length) + { + int nextSize = (_buffer.Length * 2) + count; + Array.Resize(ref _buffer, nextSize); + } + } + + /// + /// Returns the buffer. The returned value will be the full buffer, even if not all of it is used. + /// + /// + public byte[] GetBuffer() + { + return _buffer; + } + + /// + /// Returns the used portion of the buffer as an ArraySegment. + /// + /// + public ArraySegment GetArraySegment() + { + return new ArraySegment(_buffer, 0, Length); + } + + /// + /// Reserves a number of bytes from current position. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Reserve(int count) + { + EnsureBufferLength(count); + Position += count; + Length = Math.Max(Length, Position); + } + + /// + /// Writes length. This method is used to make debugging easier. + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void WriteLength(int length) + { + WriteInt32(length); + } + + /// + /// Sends a packetId. + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void WritePacketId(PacketId pid) + { + WriteUInt16((ushort)pid); + } + + /// + /// Inserts value at index within the buffer. + /// This method does not perform error checks. + /// + /// + /// + [CodegenExclude] + public void FastInsertByte(byte value, int index) + { + _buffer[index] = value; + } + + /// + /// Writes a byte. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteByte(byte value) + { + EnsureBufferLength(1); + _buffer[Position++] = value; + + Length = Math.Max(Length, Position); + } + + /// + /// Writes bytes. + /// + /// + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteBytes(byte[] value, int offset, int count) + { + EnsureBufferLength(count); + Buffer.BlockCopy(value, offset, _buffer, Position, count); + Position += count; + Length = Math.Max(Length, Position); + } + + /// + /// Writes bytes and length of bytes. + /// + /// + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteBytesAndSize(byte[] value, int offset, int count) + { + if (value == null) + { + WriteInt32(Writer.UNSET_COLLECTION_SIZE_VALUE); + } + else + { + WriteInt32(count); + WriteBytes(value, offset, count); + } + } + + /// + /// Writes all bytes in value and length of bytes. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteBytesAndSize(byte[] value) + { + int size = (value == null) ? 0 : value.Length; + // buffer might be null, so we can't use .Length in that case + WriteBytesAndSize(value, 0, size); + } + + + /// + /// Writes a sbyte. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteSByte(sbyte value) + { + EnsureBufferLength(1); + _buffer[Position++] = (byte)value; + Length = Math.Max(Length, Position); + } + + /// + /// Writes a char. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteChar(char value) + { + EnsureBufferLength(2); + _buffer[Position++] = (byte)value; + _buffer[Position++] = (byte)(value >> 8); + Length = Math.Max(Length, Position); + } + + /// + /// Writes a boolean. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteBoolean(bool value) + { + EnsureBufferLength(1); + _buffer[Position++] = (value) ? (byte)1 : (byte)0; + Length = Math.Max(Length, Position); + } + + /// + /// Writes a uint16. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteUInt16(ushort value) + { + EnsureBufferLength(2); + _buffer[Position++] = (byte)value; + _buffer[Position++] = (byte)(value >> 8); + Length = Math.Max(Length, Position); + } + + /// + /// Writes a int16. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteInt16(short value) + { + EnsureBufferLength(2); + _buffer[Position++] = (byte)value; + _buffer[Position++] = (byte)(value >> 8); + Length = Math.Max(Length, Position); + } + + /// + /// Writes a int32. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteInt32(int value, AutoPackType packType = AutoPackType.Packed) + { + if (packType == AutoPackType.Packed) + WritePackedWhole(ZigZagEncode((ulong)value)); + else + WriteUInt32((uint)value, packType); + } + /// + /// Writes a uint32. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteUInt32(uint value, AutoPackType packType = AutoPackType.Packed) + { + if (packType == AutoPackType.Unpacked) + { + EnsureBufferLength(4); + WriterExtensions.WriteUInt32(_buffer, value, ref Position); + Length = Math.Max(Length, Position); + } + else + { + WritePackedWhole(value); + } + } + + /// + /// Writes an int64. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteInt64(long value, AutoPackType packType = AutoPackType.Packed) + { + if (packType == AutoPackType.Packed) + WritePackedWhole(ZigZagEncode((ulong)value)); + else + WriteUInt64((ulong)value, packType); + } + /// + /// Writes a uint64. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteUInt64(ulong value, AutoPackType packType = AutoPackType.Packed) + { + if (packType == AutoPackType.Unpacked) + { + EnsureBufferLength(8); + _buffer[Position++] = (byte)value; + _buffer[Position++] = (byte)(value >> 8); + _buffer[Position++] = (byte)(value >> 16); + _buffer[Position++] = (byte)(value >> 24); + _buffer[Position++] = (byte)(value >> 32); + _buffer[Position++] = (byte)(value >> 40); + _buffer[Position++] = (byte)(value >> 48); + _buffer[Position++] = (byte)(value >> 56); + + Length = Math.Max(Position, Length); + } + else + { + WritePackedWhole(value); + } + } + + /// + /// Writes a single (float). + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteSingle(float value, AutoPackType packType = AutoPackType.Unpacked) + { + if (packType == AutoPackType.Unpacked) + { + UIntFloat converter = new UIntFloat { FloatValue = value }; + WriteUInt32(converter.UIntValue, AutoPackType.Unpacked); + } + else + { + long converter = (long)(value * 100f); + WritePackedWhole((ulong)converter); + } + } + + /// + /// Writes a double. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteDouble(double value) + { + UIntDouble converter = new UIntDouble { DoubleValue = value }; + WriteUInt64(converter.LongValue, AutoPackType.Unpacked); + } + + /// + /// Writes a decimal. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteDecimal(decimal value) + { + UIntDecimal converter = new UIntDecimal { DecimalValue = value }; + WriteUInt64(converter.LongValue1, AutoPackType.Unpacked); + WriteUInt64(converter.LongValue2, AutoPackType.Unpacked); + } + + /// + /// Writes a string. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteString(string value) + { + if (value == null) + { + WriteInt32(Writer.UNSET_COLLECTION_SIZE_VALUE); + return; + } + else if (value.Length == 0) + { + WriteInt32(0); + return; + } + + /* Resize string buffer as needed. There's no harm in + * increasing buffer on writer side because sender will + * never intentionally inflict allocations on itself. + * Reader ensures string count cannot exceed received + * packet size. */ + int size; + byte[] stringBuffer = WriterStatics.GetStringBuffer(value, out size); + WriteInt32(size); + WriteBytes(stringBuffer, 0, size); + } + + /// + /// Writes a byte ArraySegment and it's size. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteArraySegmentAndSize(ArraySegment value) + { + WriteBytesAndSize(value.Array, value.Offset, value.Count); + } + + /// + /// Writes an ArraySegment without size. + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteArraySegment(ArraySegment value) + { + WriteBytes(value.Array, value.Offset, value.Count); + } + + /// + /// Writes a Vector2. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteVector2(Vector2 value) + { + UIntFloat converter; + converter = new UIntFloat { FloatValue = value.x }; + WriteUInt32(converter.UIntValue, AutoPackType.Unpacked); + converter = new UIntFloat { FloatValue = value.y }; + WriteUInt32(converter.UIntValue, AutoPackType.Unpacked); + } + + /// + /// Writes a Vector3 + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteVector3(Vector3 value) + { + UIntFloat converter; + converter = new UIntFloat { FloatValue = value.x }; + WriteUInt32(converter.UIntValue, AutoPackType.Unpacked); + converter = new UIntFloat { FloatValue = value.y }; + WriteUInt32(converter.UIntValue, AutoPackType.Unpacked); + converter = new UIntFloat { FloatValue = value.z }; + WriteUInt32(converter.UIntValue, AutoPackType.Unpacked); + } + + /// + /// Writes a Vector4. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteVector4(Vector4 value) + { + UIntFloat converter; + converter = new UIntFloat { FloatValue = value.x }; + WriteUInt32(converter.UIntValue, AutoPackType.Unpacked); + converter = new UIntFloat { FloatValue = value.y }; + WriteUInt32(converter.UIntValue, AutoPackType.Unpacked); + converter = new UIntFloat { FloatValue = value.z }; + WriteUInt32(converter.UIntValue, AutoPackType.Unpacked); + converter = new UIntFloat { FloatValue = value.w }; + WriteUInt32(converter.UIntValue, AutoPackType.Unpacked); + } + + /// + /// Writes a Vector2Int. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteVector2Int(Vector2Int value, AutoPackType packType = AutoPackType.Packed) + { + WriteInt32(value.x, packType); + WriteInt32(value.y, packType); + } + + /// + /// Writes a Vector3Int. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteVector3Int(Vector3Int value, AutoPackType packType = AutoPackType.Packed) + { + WriteInt32(value.x, packType); + WriteInt32(value.y, packType); + WriteInt32(value.z, packType); + } + + /// + /// Writes a Color. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteColor(Color value, AutoPackType packType = AutoPackType.Packed) + { + if (packType == AutoPackType.Unpacked) + { + WriteSingle(value.r); + WriteSingle(value.g); + WriteSingle(value.b); + WriteSingle(value.a); + } + else + { + EnsureBufferLength(4); + _buffer[Position++] = (byte)(value.r * 100f); + _buffer[Position++] = (byte)(value.g * 100f); + _buffer[Position++] = (byte)(value.b * 100f); + _buffer[Position++] = (byte)(value.a * 100f); + + Length = Math.Max(Length, Position); + } + } + + /// + /// Writes a Color32. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteColor32(Color32 value) + { + EnsureBufferLength(4); + _buffer[Position++] = value.r; + _buffer[Position++] = value.g; + _buffer[Position++] = value.b; + _buffer[Position++] = value.a; + + Length = Math.Max(Length, Position); + } + + /// + /// Writes a Quaternion. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteQuaternion(Quaternion value, AutoPackType packType = AutoPackType.Packed) + { + if (packType == AutoPackType.Packed) + { + EnsureBufferLength(4); + uint result = Quaternion32Compression.Compress(value); + WriterExtensions.WriteUInt32(_buffer, result, ref Position); + Length = Math.Max(Length, Position); + } + else if (packType == AutoPackType.PackedLess) + { + EnsureBufferLength(8); + ulong result = Quaternion64Compression.Compress(value); + WriterExtensions.WriteUInt64(_buffer, result, ref Position); + Length = Math.Max(Length, Position); + } + else + { + EnsureBufferLength(16); + WriteSingle(value.x); + WriteSingle(value.y); + WriteSingle(value.z); + WriteSingle(value.w); + } + } + + /// + /// Writes a rect. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteRect(Rect value) + { + WriteSingle(value.xMin); + WriteSingle(value.yMin); + WriteSingle(value.width); + WriteSingle(value.height); + } + + /// + /// Writes a plane. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WritePlane(Plane value) + { + WriteVector3(value.normal); + WriteSingle(value.distance); + } + + /// + /// Writes a Ray. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteRay(Ray value) + { + WriteVector3(value.origin); + WriteVector3(value.direction); + } + + /// + /// Writes a Ray2D. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteRay2D(Ray2D value) + { + WriteVector2(value.origin); + WriteVector2(value.direction); + } + + + /// + /// Writes a Matrix4x4. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteMatrix4x4(Matrix4x4 value) + { + WriteSingle(value.m00); + WriteSingle(value.m01); + WriteSingle(value.m02); + WriteSingle(value.m03); + WriteSingle(value.m10); + WriteSingle(value.m11); + WriteSingle(value.m12); + WriteSingle(value.m13); + WriteSingle(value.m20); + WriteSingle(value.m21); + WriteSingle(value.m22); + WriteSingle(value.m23); + WriteSingle(value.m30); + WriteSingle(value.m31); + WriteSingle(value.m32); + WriteSingle(value.m33); + } + + /// + /// Writes a Guid. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteGuidAllocated(System.Guid value) + { + byte[] data = value.ToByteArray(); + WriteBytes(data, 0, data.Length); + } + + /// + /// Writes a GameObject. GameObject must be spawned over the network already or be a prefab with a NetworkObject attached. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteGameObject(GameObject go) + { + if (go == null) + { + WriteNetworkObject(null); + } + else + { + NetworkObject nob = go.GetComponent(); + WriteNetworkObject(nob); + } + } + + /// + /// Writes a Transform. Transform must be spawned over the network already or be a prefab with a NetworkObject attached. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteTransform(Transform t) + { + if (t == null) + { + WriteNetworkObject(null); + } + else + { + NetworkObject nob = t.GetComponent(); + WriteNetworkObject(nob); + } + } + + + /// + /// Writes a NetworkObject. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteNetworkObject(NetworkObject nob) + { + WriteNetworkObject(nob, false); + } + + /// + /// Writes a NetworkObject.ObjectId. + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteNetworkObjectId(NetworkObject nob) + { + if (nob == null) + WriteUInt16(NetworkObject.UNSET_OBJECTID_VALUE); + else + WriteNetworkObjectId(nob.ObjectId); + } + + /// + /// Writes a NetworkObject while optionally including the initialization order. + /// + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void WriteNetworkObject(NetworkObject nob, bool forSpawn) + { + if (nob == null) + { + WriteUInt16(NetworkObject.UNSET_OBJECTID_VALUE); + } + else + { + bool spawned = nob.IsSpawned; + if (spawned) + WriteNetworkObjectId(nob.ObjectId); + else + WriteNetworkObjectId(nob.PrefabId); + + //Has to be written after objectId since that's expected first in reader. + if (forSpawn) + { + WriteUInt16(nob.SpawnableCollectionId); + WriteSByte(nob.GetInitializeOrder()); + } + + WriteBoolean(spawned); + } + } + + /// + /// Writes a NetworkObject for a despawn message. + /// + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void WriteNetworkObjectForDespawn(NetworkObject nob, DespawnType dt) + { + WriteNetworkObjectId(nob.ObjectId); + WriteByte((byte)dt); + } + + + /// + /// Writes an objectId. + /// + [CodegenExclude] + public void WriteNetworkObjectId(int objectId) + { + WriteUInt16((ushort)objectId); + } + + /// + /// Writes a NetworkObject for a spawn packet. + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void WriteNetworkObjectForSpawn(NetworkObject nob) + { + WriteNetworkObject(nob, true); + } + + /// + /// Writes a NetworkBehaviour. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteNetworkBehaviour(NetworkBehaviour nb) + { + if (nb == null) + { + WriteNetworkObject(null); + WriteByte(0); + } + else + { + WriteNetworkObject(nb.NetworkObject); + WriteByte(nb.ComponentIndex); + } + } + + /// + /// Writes a NetworkBehaviourId. + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteNetworkBehaviourId(NetworkBehaviour nb) + { + if (nb == null) + { + WriteNetworkObjectId(null); + } + else + { + WriteNetworkObjectId(nb.NetworkObject); + WriteByte(nb.ComponentIndex); + } + } + + /// + /// Writes a DateTime. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteDateTime(DateTime dt) + { + WriteInt64(dt.ToBinary()); + } + + /// + /// Writes a transport channel. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteChannel(Channel channel) + { + WriteByte((byte)channel); + } + + /// + /// Writes a NetworkConnection. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteNetworkConnection(NetworkConnection connection) + { + int value = (connection == null) ? NetworkConnection.UNSET_CLIENTID_VALUE : connection.ClientId; + WriteInt16((short)value); + } + + /// + /// Writes a short for a connectionId. + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteNetworkConnectionId(short id) + { + WriteInt16(id); + } + + /// + /// Writes a ListCache. + /// + /// ListCache to write. + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteListCache(ListCache lc) + { + WriteList(lc.Collection); + } + /// + /// Writes a list. + /// + /// Collection to write. + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteList(List value) + { + if (value == null) + WriteList(null, 0, 0); + else + WriteList(value, 0, value.Count); + } + #region Packed writers. + /// + /// ZigZag encode an integer. Move the sign bit to the right. + /// + [CodegenExclude] + public ulong ZigZagEncode(ulong value) + { + if (value >> 63 > 0) + return ~(value << 1) | 1; + return value << 1; + } + /// + /// Writes a packed whole number. + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WritePackedWhole(ulong value) + { + if (value < 0x80UL) + { + EnsureBufferLength(1); + _buffer[Position++] = (byte)(value & 0x7F); + } + else if (value < 0x4000UL) + { + EnsureBufferLength(2); + _buffer[Position++] = (byte)(0x80 | (value & 0x7F)); + _buffer[Position++] = (byte)((value >> 7) & 0x7F); + } + else if (value < 0x200000UL) + { + EnsureBufferLength(3); + _buffer[Position++] = (byte)(0x80 | (value & 0x7F)); + _buffer[Position++] = (byte)(0x80 | ((value >> 7) & 0x7F)); + _buffer[Position++] = (byte)((value >> 14) & 0x7F); + } + else if (value < 0x10000000UL) + { + EnsureBufferLength(4); + _buffer[Position++] = (byte)(0x80 | (value & 0x7F)); + _buffer[Position++] = (byte)(0x80 | ((value >> 7) & 0x7F)); + _buffer[Position++] = (byte)(0x80 | ((value >> 14) & 0x7F)); + _buffer[Position++] = (byte)((value >> 21) & 0x7F); + } + else if (value < 0x100000000UL) + { + EnsureBufferLength(5); + _buffer[Position++] = (byte)(0x80 | (value & 0x7F)); + _buffer[Position++] = (byte)(0x80 | ((value >> 7) & 0x7F)); + _buffer[Position++] = (byte)(0x80 | ((value >> 14) & 0x7F)); + _buffer[Position++] = (byte)(0x80 | ((value >> 21) & 0x7F)); + _buffer[Position++] = (byte)((value >> 28) & 0x0F); + } + else if (value < 0x10000000000UL) + { + EnsureBufferLength(6); + _buffer[Position++] = (byte)(0x80 | (value & 0x7F)); + _buffer[Position++] = (byte)(0x80 | ((value >> 7) & 0x7F)); + _buffer[Position++] = (byte)(0x80 | ((value >> 14) & 0x7F)); + _buffer[Position++] = (byte)(0x80 | ((value >> 21) & 0x7F)); + _buffer[Position++] = (byte)(0x10 | ((value >> 28) & 0x0F)); + _buffer[Position++] = (byte)((value >> 32) & 0xFF); + } + else if (value < 0x1000000000000UL) + { + EnsureBufferLength(7); + _buffer[Position++] = (byte)(0x80 | (value & 0x7F)); + _buffer[Position++] = (byte)(0x80 | ((value >> 7) & 0x7F)); + _buffer[Position++] = (byte)(0x80 | ((value >> 14) & 0x7F)); + _buffer[Position++] = (byte)(0x80 | ((value >> 21) & 0x7F)); + _buffer[Position++] = (byte)(0x20 | ((value >> 28) & 0x0F)); + _buffer[Position++] = (byte)((value >> 32) & 0xFF); + _buffer[Position++] = (byte)((value >> 40) & 0xFF); + } + else if (value < 0x100000000000000UL) + { + EnsureBufferLength(8); + _buffer[Position++] = (byte)(0x80 | (value & 0x7F)); + _buffer[Position++] = (byte)(0x80 | ((value >> 7) & 0x7F)); + _buffer[Position++] = (byte)(0x80 | ((value >> 14) & 0x7F)); + _buffer[Position++] = (byte)(0x80 | ((value >> 21) & 0x7F)); + _buffer[Position++] = (byte)(0x30 | ((value >> 28) & 0x0F)); + _buffer[Position++] = (byte)((value >> 32) & 0xFF); + _buffer[Position++] = (byte)((value >> 40) & 0xFF); + _buffer[Position++] = (byte)((value >> 48) & 0xFF); + } + else + { + EnsureBufferLength(9); + _buffer[Position++] = (byte)(0x80 | (value & 0x7F)); + _buffer[Position++] = (byte)(0x80 | ((value >> 7) & 0x7F)); + _buffer[Position++] = (byte)(0x80 | ((value >> 14) & 0x7F)); + _buffer[Position++] = (byte)(0x80 | ((value >> 21) & 0x7F)); + _buffer[Position++] = (byte)(0x40 | ((value >> 28) & 0x0F)); + _buffer[Position++] = (byte)((value >> 32) & 0xFF); + _buffer[Position++] = (byte)((value >> 40) & 0xFF); + _buffer[Position++] = (byte)((value >> 48) & 0xFF); + _buffer[Position++] = (byte)((value >> 56) & 0xFF); + } + + Length = Math.Max(Length, Position); + } + #endregion + + #region Generators. + /// + /// Writes a list. + /// + /// Collection to write. + /// Offset to begin at. + /// Entries to write. + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteList(List value, int offset, int count) + { + if (value == null) + { + WriteInt32(Writer.UNSET_COLLECTION_SIZE_VALUE); + } + else + { + //Make sure values cannot cause out of bounds. + if ((offset + count > value.Count)) + count = 0; + + WriteInt32(count); + for (int i = 0; i < count; i++) + Write(value[i + offset]); + } + } + /// + /// Writes a list. + /// + /// Collection to write. + /// Offset to begin at. + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteList(List value, int offset) + { + if (value == null) + WriteList(null, 0, 0); + else + WriteList(value, offset, value.Count - offset); + } + + /// + /// Writes a replication to the server. + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void WriteReplicate(List values, int offset) + { + /* COUNT + * + * Each Entry: + * 0 if the same as previous. + * 1 if default. */ + int collectionCount = values.Count; + //Replicate list will never be null, no need to write null check. + //Number of entries being written. + byte count = (byte)(collectionCount - offset); + WriteByte(count); + + //Get comparer. + Func compareDel = GeneratedComparer.Compare; + Func isDefaultDel = GeneratedComparer.IsDefault; + if (compareDel == null || isDefaultDel == null) + { + LogError($"ReplicateComparers not found for type {typeof(T).FullName}"); + return; + } + + T lastData = default; + /* It's possible to save more bytes by writing that they are all the same. + * Run a check, and if they are all the same then only write + * the first data with the same indicator code. */ + byte fullPackType = 0; + bool repeating = true; + bool allDefault = true; + for (int i = offset; i < collectionCount; i++) + { + T v = values[i]; + if (!isDefaultDel(v)) + allDefault = false; + + //Only check if i is larger than offset, giving us something in the past to check. + if (i > offset) + { + //Not repeating. + bool match = compareDel.Invoke(v, values[i - 1]); + if (!match) + { + repeating = false; + break; + } + } + + } + + if (allDefault) + fullPackType = REPLICATE_ALL_DEFAULT_BYTE; + else if (repeating) + fullPackType = REPLICATE_REPEATING_BYTE; + WriteByte(fullPackType); + + //If repeating only write the first entry. + if (repeating) + { + //Only write if not default. + if (!allDefault) + Write(values[offset]); + } + //Otherwise check each entry for differences. + else + { + for (int i = offset; i < collectionCount; i++) + { + T v = values[i]; + bool isDefault = isDefaultDel.Invoke(v); + //Default data, easy exit on writes. + if (isDefault) + { + WriteByte(REPLICATE_DEFAULT_BYTE); + } + //Data is not default. + else + { + //Same as last data. + bool match = compareDel.Invoke(v, lastData); + if (match) + { + WriteByte(REPLICATE_DUPLICATE_BYTE); + } + else + { + WriteByte(REPLICATE_UNIQUE_BYTE); + Write(v); + lastData = v; + } + } + } + } + } + + /// + /// Writes an array. + /// + /// Collection to write. + /// Offset to begin at. + /// Entries to write. + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteArray(T[] value, int offset, int count) + { + if (value == null) + { + WriteInt32(Writer.UNSET_COLLECTION_SIZE_VALUE); + } + else + { + //If theres no values, or offset exceeds count then write 0 for count. + if (value.Length == 0 || (offset >= count)) + { + WriteInt32(0); + } + else + { + WriteInt32(count); + for (int i = offset; i < count; i++) + Write(value[i]); + } + } + } + /// + /// Writes an array. + /// + /// Collection to write. + /// Offset to begin at. + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteArray(T[] value, int offset) + { + if (value == null) + WriteArray(null, 0, 0); + else + WriteArray(value, offset, value.Length - offset); + } + /// + /// Writes an array. + /// + /// Collection to write. + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteArray(T[] value) + { + if (value == null) + WriteArray(null, 0, 0); + else + WriteArray(value, 0, value.Length); + } + + + /// + /// Writers any supported type. + /// + /// + /// + [CodegenExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(T value) + { + System.Type type = typeof(T); + if (IsAutoPackType(type, out AutoPackType packType)) + { + Action del = GenericWriter.WriteAutoPack; + if (del == null) + LogError(GetLogMessage()); + else + del.Invoke(this, value, packType); + } + else + { + Action del = GenericWriter.Write; + if (del == null) + LogError(GetLogMessage()); + else + del.Invoke(this, value); + } + + string GetLogMessage() => $"Write method not found for {type.FullName}. Use a supported type or create a custom serializer."; + } + + /// + /// Logs an error. + /// + /// + private void LogError(string msg) + { + if (NetworkManager == null) + NetworkManager.StaticLogError(msg); + else + NetworkManager.LogError(msg); + } + + /// + /// Returns if T takes AutoPackType argument. + /// + /// Outputs the default pack type for T. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsAutoPackType(out AutoPackType packType) + { + System.Type type = typeof(T); + return IsAutoPackType(type, out packType); + } + internal static bool IsAutoPackType(Type type, out AutoPackType packType) + { + if (WriterExtensions.DefaultPackedTypes.Contains(type)) + { + packType = AutoPackType.Packed; + return true; + } + else if (type == typeof(float)) + { + packType = AutoPackType.Unpacked; + return true; + } + else + { + packType = AutoPackType.Unpacked; + return false; + } + } + #endregion + + } +} diff --git a/Assets/FishNet/Runtime/Serializing/Writer.cs.meta b/Assets/FishNet/Runtime/Serializing/Writer.cs.meta new file mode 100644 index 0000000..bfbcd29 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Writer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e2633f927065d9d43b8a4da09240266c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Serializing/WriterExtensions.cs b/Assets/FishNet/Runtime/Serializing/WriterExtensions.cs new file mode 100644 index 0000000..8f727c7 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/WriterExtensions.cs @@ -0,0 +1,113 @@ +using FishNet.Connection; +using FishNet.Documenting; +using FishNet.Object; +using FishNet.Serializing.Helping; +using FishNet.Transporting; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace FishNet.Serializing +{ + + /// + /// Extensions to Write methods. Used by Write. + /// + [APIExclude] + public static class WriterExtensions + { + + /// + /// Types which are are set to auto pack by default. + /// + internal static HashSet DefaultPackedTypes = new HashSet(); + + static WriterExtensions() + { + DefaultPackedTypes.Add(typeof(int)); + DefaultPackedTypes.Add(typeof(uint)); + DefaultPackedTypes.Add(typeof(long)); + DefaultPackedTypes.Add(typeof(ulong)); + DefaultPackedTypes.Add(typeof(Color)); + DefaultPackedTypes.Add(typeof(Vector2Int)); + DefaultPackedTypes.Add(typeof(Vector3Int)); + DefaultPackedTypes.Add(typeof(Quaternion)); + } + + /// + /// Writes value to dst without error checking. + /// + [CodegenExclude] + internal static void WriteUInt32(byte[] dst, uint value, ref int position) + { + dst[position++] = (byte)value; + dst[position++] = (byte)(value >> 8); + dst[position++] = (byte)(value >> 16); + dst[position++] = (byte)(value >> 24); + } + /// + /// Writes value to dst without error checking. + /// + [CodegenExclude] + internal static void WriteUInt64(byte[] dst, ulong value, ref int position) + { + dst[position++] = (byte)value; + dst[position++] = (byte)(value >> 8); + dst[position++] = (byte)(value >> 16); + dst[position++] = (byte)(value >> 24); + dst[position++] = (byte)(value >> 32); + dst[position++] = (byte)(value >> 40); + dst[position++] = (byte)(value >> 48); + dst[position++] = (byte)(value >> 56); + } + + + //public static void WriteDictionary(this Writer writer, Dictionary dict) => writer.WriteDictionary(dict); + //public static void WriteByte(this Writer writer, byte value) => writer.WriteByte(value); + //[CodegenExclude] + //public static void WriteBytes(this Writer writer, byte[] buffer, int offset, int count) => writer.WriteBytes(buffer, offset, count); + //[CodegenExclude] + //public static void WriteBytesAndSize(this Writer writer, byte[] buffer, int offset, int count) => writer.WriteBytesAndSize(buffer, offset, count); + //public static void WriteBytesAndSize(this Writer writer, byte[] value) => writer.WriteBytesAndSize(value); + + //public static void WriteSByte(this Writer writer, sbyte value) => writer.WriteSByte(value); + //public static void WriteChar(this Writer writer, char value) => writer.WriteChar(value); + //public static void WriteBoolean(this Writer writer, bool value) => writer.WriteBoolean(value); + //public static void WriteUInt16(this Writer writer, ushort value) => writer.WriteUInt16(value); + //public static void WriteInt16(this Writer writer, short value) => writer.WriteInt16(value); + //public static void WriteInt32(this Writer writer, int value, AutoPackType packType = AutoPackType.Packed) => writer.WriteInt32(value, packType); + //public static void WriteUInt32(this Writer writer, uint value, AutoPackType packType = AutoPackType.Packed) => writer.WriteUInt32(value, packType); + //public static void WriteInt64(this Writer writer, long value, AutoPackType packType = AutoPackType.Packed) => writer.WriteInt64(value, packType); + //public static void WriteUInt64(this Writer writer, ulong value, AutoPackType packType = AutoPackType.Packed) => writer.WriteUInt64(value, packType); + //public static void WriteSingle(this Writer writer, float value, AutoPackType packType = AutoPackType.Unpacked) => writer.WriteSingle(value, packType); + //public static void WriteDouble(this Writer writer, double value) => writer.WriteDouble(value); + //public static void WriteDecimal(this Writer writer, decimal value) => writer.WriteDecimal(value); + //public static void WriteString(this Writer writer, string value) => writer.WriteString(value); + //public static void WriteArraySegmentAndSize(this Writer writer, ArraySegment value) => writer.WriteArraySegmentAndSize(value); + //[CodegenExclude] + //public static void WriteArraySegment(this Writer writer, ArraySegment value) => writer.WriteArraySegment(value); + //public static void WriteVector2(this Writer writer, Vector2 value) => writer.WriteVector2(value); + //public static void WriteVector3(this Writer writer, Vector3 value) => writer.WriteVector3(value); + //public static void WriteVector4(this Writer writer, Vector4 value) => writer.WriteVector4(value); + //public static void WriteVector2Int(this Writer writer, Vector2Int value, AutoPackType packType = AutoPackType.Packed) => writer.WriteVector2Int(value, packType); + //public static void WriteVector3Int(this Writer writer, Vector3Int value, AutoPackType packType = AutoPackType.Packed) => writer.WriteVector3Int(value, packType); + //public static void WriteColor(this Writer writer, Color value, AutoPackType packType) => writer.WriteColor(value, packType); + //public static void WriteColor32(this Writer writer, Color32 value) => writer.WriteColor32(value); + //public static void WriteQuaternion(this Writer writer, Quaternion value, AutoPackType packType = AutoPackType.Packed) => writer.WriteQuaternion(value, packType); + //public static void WriteRect(this Writer writer, Rect value) => writer.WriteRect(value); + //public static void WritePlane(this Writer writer, Plane value) => writer.WritePlane(value); + //public static void WriteRay(this Writer writer, Ray value) => writer.WriteRay(value); + //public static void WriteRay2D(this Writer writer, Ray2D value) => writer.WriteRay2D(value); + //public static void WriteMatrix4x4(this Writer writer, Matrix4x4 value) => writer.WriteMatrix4x4(value); + //public static void WriteGuidAllocated(this Writer writer, System.Guid value) => writer.WriteGuidAllocated(value); + //public static void WriteGameObject(this Writer writer, GameObject value) => writer.WriteGameObject(value); + //public static void WriteTransform(this Writer writer, Transform value) => writer.WriteTransform(value); + //public static void WriteNetworkObject(this Writer writer, NetworkObject value) => writer.WriteNetworkObject(value); + //public static void WriteNetworkBehaviour(this Writer writer, NetworkBehaviour value) => writer.WriteNetworkBehaviour(value); + //public static void WriteChannel(this Writer writer, Channel value) => writer.WriteChannel(value); + //public static void WriteNetworkConnection(this Writer writer, NetworkConnection value) => writer.WriteNetworkConnection(value); + //[CodegenExclude] + //public static void Write(this Writer writer, T value) => writer.Write(value); + + } +} diff --git a/Assets/FishNet/Runtime/Serializing/WriterExtensions.cs.meta b/Assets/FishNet/Runtime/Serializing/WriterExtensions.cs.meta new file mode 100644 index 0000000..a2229b9 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/WriterExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 493e880d63e9372449c0f0e63890aaef +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Serializing/WriterPool.cs b/Assets/FishNet/Runtime/Serializing/WriterPool.cs new file mode 100644 index 0000000..8a6f250 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/WriterPool.cs @@ -0,0 +1,159 @@ +using FishNet.Managing; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace FishNet.Serializing +{ + /// + /// Writer which is reused to save on garbage collection and performance. + /// + public sealed class PooledWriter : Writer, IDisposable + { + public void Dispose() => WriterPool.Recycle(this); + public void DisposeLength() => WriterPool.RecycleLength(this); + } + + /// + /// Collection of PooledWriter. Stores and gets PooledWriter. + /// + public static class WriterPool + { + #region Private. + /// + /// Pool of writers where length is the minimum and increased at runtime. + /// + private static readonly Stack _pool = new Stack(); + /// + /// Pool of writers where length is of minimum key and may be increased at runtime. + /// + private static readonly Dictionary> _lengthPool = new Dictionary>(); + #endregion + + #region Const. + /// + /// Length of each bracket when using the length based writer pool. + /// + internal const int LENGTH_BRACKET = 1000; + #endregion + + /// + /// Gets a writer from the pool. + /// + public static PooledWriter GetWriter(NetworkManager networkManager) + { + PooledWriter result = (_pool.Count > 0) ? _pool.Pop() : new PooledWriter(); + result.Reset(networkManager); + return result; + } + /// + /// Gets a writer from the pool. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PooledWriter GetWriter() + { + return GetWriter(null); + } + + /// + /// Gets which index to use for length based pooled readers based on length. + /// + private static int GetDictionaryIndex(int length) + { + return (length / LENGTH_BRACKET); + } + /// + /// Gets the next writer in the pool of minimum length. + /// + /// Minimum length the writer buffer must be. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PooledWriter GetWriter(int length) + { + return GetWriter(null, length); + } + /// + /// Gets the next writer in the pool of minimum length. + /// + /// Minimum length the writer buffer must be. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PooledWriter GetWriter(NetworkManager networkManager, int length) + { + //Ensure length is the minimum. + if (length < LENGTH_BRACKET) + length = LENGTH_BRACKET; + + /* The index returned will be for writers which have + * length as a minimum capacity. + * EG: if length is 1200 / 1000 (length_bracket) result + * will be index 1. Index 0 will be up to 1000, while + * index 1 will be up to 2000. */ + int dictIndex = GetDictionaryIndex(length); + Stack stack; + //There is already one pooled. + if (_lengthPool.TryGetValue(dictIndex, out stack) && stack.Count > 0) + { + PooledWriter result = stack.Pop(); + result.Reset(networkManager); + return result; + } + //Not pooled yet. + else + { + //Get any ol' writer. + PooledWriter writer = GetWriter(networkManager); + /* Ensure length to fill it's bracket. + * Increase index by 1 since 0 index would + * just return 0 as the capacity. */ + int requiredCapacity = (dictIndex + 1) * LENGTH_BRACKET; + writer.EnsureBufferCapacity(requiredCapacity); + return writer; + } + } + + /// + /// Returns a writer to the appropriate length pool. + /// Writers must be a minimum of 1000 bytes in length to be sorted by length. + /// Writers which do not meet the minimum will be resized to 1000 bytes. + /// + public static void RecycleLength(PooledWriter writer) + { + int capacity = writer.Capacity; + /* If capacity is less than 1000 then the writer + * does not meet the minimum length bracket. This should never + * be the case unless the user perhaps manually calls this method. */ + if (capacity < LENGTH_BRACKET) + { + capacity = LENGTH_BRACKET; + writer.EnsureBufferCapacity(LENGTH_BRACKET); + } + + /* When getting the recycle index subtract one from + * the dictionary index. This is because the writer being + * recycled must meet the minimum for that index. + * EG: if LENGTH_BRACKET is 1000.... + * 1200 / 1000 = 1(after flooring). + * However, each incremeent in index should have a capacity + * of 1000, so index 1 should have a minimum capacity of 2000, + * which 1200 does not meet. By subtracting 1 from the index + * 1200 will now be placed in index 0 meeting the capacity for that index. */ + int dictIndex = GetDictionaryIndex(capacity) - 1; + Stack stack; + if (!_lengthPool.TryGetValue(dictIndex, out stack)) + { + stack = new Stack(); + _lengthPool[dictIndex] = stack; + } + + stack.Push(writer); + } + + + /// + /// Returns a writer to the pool. + /// + public static void Recycle(PooledWriter writer) + { + _pool.Push(writer); + } + } +} diff --git a/Assets/FishNet/Runtime/Serializing/WriterPool.cs.meta b/Assets/FishNet/Runtime/Serializing/WriterPool.cs.meta new file mode 100644 index 0000000..2507ad0 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/WriterPool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 99b13ccd24eb7264abf67780ef86e44a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Serializing/WriterStatics.cs b/Assets/FishNet/Runtime/Serializing/WriterStatics.cs new file mode 100644 index 0000000..6a3cc02 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/WriterStatics.cs @@ -0,0 +1,61 @@ +using FishNet.Documenting; +using System; +using System.Text; + +namespace FishNet.Serializing +{ + + /// + /// Writes data to a buffer. + /// + [APIExclude] + internal class WriterStatics + { + /* Since serializing occurs on the main thread this value may + * be shared among all writers. //multithread + */ + + #region Private. + /// + /// Encoder for strings. + /// + private static readonly UTF8Encoding _encoding = new UTF8Encoding(false, true); + /// + /// StringBuffer to use with encoding. + /// + private static byte[] _stringBuffer = new byte[64]; + #endregion + + /// + /// Gets the string buffer ensuring proper length, and outputs size in bytes of string. + /// + public static byte[] GetStringBuffer(string str, out int size) + { + int strLength = str.Length; + int valueMaxBytes = _encoding.GetMaxByteCount(strLength); + if (valueMaxBytes >= _stringBuffer.Length) + { + int nextSize = (_stringBuffer.Length * 2) + valueMaxBytes; + Array.Resize(ref _stringBuffer, nextSize); + } + + size = _encoding.GetBytes(str, 0, strLength, _stringBuffer, 0); + return _stringBuffer; + + } + /// + /// Ensures the string buffer is of a minimum length and returns the buffer. + /// + public static byte[] GetStringBuffer(string str) + { + int valueMaxBytes = _encoding.GetMaxByteCount(str.Length); + if (valueMaxBytes >= _stringBuffer.Length) + { + int nextSize = (_stringBuffer.Length * 2) + valueMaxBytes; + Array.Resize(ref _stringBuffer, nextSize); + } + + return _stringBuffer; + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/WriterStatics.cs.meta b/Assets/FishNet/Runtime/Serializing/WriterStatics.cs.meta new file mode 100644 index 0000000..b690420 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/WriterStatics.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 264951af6d87de34e8809b550a7956b1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting.meta b/Assets/FishNet/Runtime/Transporting.meta new file mode 100644 index 0000000..b4e33ce --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 693688363b7a8f746a35a5a6168d82b9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: '' + assetBundleName: '' + assetBundleVariant: '' diff --git a/Assets/FishNet/Runtime/Transporting/Channels.cs b/Assets/FishNet/Runtime/Transporting/Channels.cs new file mode 100644 index 0000000..195adef --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Channels.cs @@ -0,0 +1,19 @@ +namespace FishNet.Transporting +{ + /// + /// Channel which data is sent or received. + /// + public enum Channel : byte + { + /// + /// Data will be sent ordered reliable. + /// + Reliable = 0, + /// + /// Data will be sent unreliable. + /// + Unreliable = 1 + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Transporting/Channels.cs.meta b/Assets/FishNet/Runtime/Transporting/Channels.cs.meta new file mode 100644 index 0000000..1227bfa --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Channels.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7cd503d67a974984385164c53bd3e518 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/ConnectionStates.cs b/Assets/FishNet/Runtime/Transporting/ConnectionStates.cs new file mode 100644 index 0000000..ba0a35b --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/ConnectionStates.cs @@ -0,0 +1,43 @@ +namespace FishNet.Transporting +{ + + /// + /// States the local connection can be in. + /// + public enum LocalConnectionState : byte + { + /// + /// Connection is fully stopped. + /// + Stopped = 0, + /// + /// Connection is starting but not yet established. + /// + Starting = 1, + /// + /// Connection is established. + /// + Started = 2, + /// + /// Connection is stopping. + /// + Stopping = 3 + } + + /// + /// States a remote client can be in. + /// + public enum RemoteConnectionState : byte + { + /// + /// Connection is fully stopped. + /// + Stopped = 0, + /// + /// Connection is established. + /// + Started = 2, + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Transporting/ConnectionStates.cs.meta b/Assets/FishNet/Runtime/Transporting/ConnectionStates.cs.meta new file mode 100644 index 0000000..cb22c93 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/ConnectionStates.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 45640e2b3919981499b359ecc2154d3f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/EventStructures.cs b/Assets/FishNet/Runtime/Transporting/EventStructures.cs new file mode 100644 index 0000000..93ef9ae --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/EventStructures.cs @@ -0,0 +1,152 @@ +using System; + +namespace FishNet.Transporting +{ + /// + /// Container about data received on the server. + /// + public struct ServerReceivedDataArgs + { + /// + /// Data received. + /// + public ArraySegment Data; + /// + /// Channel data was received on. + /// + public Channel Channel; + /// + /// ConnectionId from which client sent data, if data was received on the server. + /// + public int ConnectionId; + /// + /// Index of the transport that is for. + /// This is primarily used when supporting multiple transports. + /// + public int TransportIndex; + /// + /// Delegate to invoke after data is processed. + /// + /// + public Action FinalizeMethod; + + public ServerReceivedDataArgs(ArraySegment data, Channel channel, int connectionId, int transportIndex) + { + Data = data; + Channel = channel; + ConnectionId = connectionId; + TransportIndex = transportIndex; + FinalizeMethod = null; + } + public ServerReceivedDataArgs(ArraySegment data, Channel channel, int connectionId, int transportIndex, Action finalizeMethod) + { + Data = data; + Channel = channel; + ConnectionId = connectionId; + TransportIndex = transportIndex; + FinalizeMethod = finalizeMethod; + } + } + + + /// + /// Container about data received on the local client. + /// + public struct ClientReceivedDataArgs + { + /// + /// Data received. + /// + public ArraySegment Data; + /// + /// Channel data was received on. + /// + public Channel Channel; + /// + /// Index of the transport that is for. + /// This is primarily used when supporting multiple transports. + /// + public int TransportIndex; + + public ClientReceivedDataArgs(ArraySegment data, Channel channel, int transportIndex) + { + Data = data; + Channel = channel; + TransportIndex = transportIndex; + } + } + + + + /// + /// Container about a connection state change for a client. + /// + public struct RemoteConnectionStateArgs + { + /// + /// Index of the transport that is for. + /// This is primarily used when supporting multiple transports. + /// + public int TransportIndex; + /// + /// New connection state. + /// + public RemoteConnectionState ConnectionState; + /// + /// ConnectionId for which client the state changed. Will be -1 if ConnectionState was for the local server. + /// + public int ConnectionId; + + public RemoteConnectionStateArgs(RemoteConnectionState connectionState, int connectionId, int transportIndex) + { + ConnectionState = connectionState; + ConnectionId = connectionId; + TransportIndex = transportIndex; + } + } + + /// + /// Container about a connection state change for the server. + /// + public struct ServerConnectionStateArgs + { + /// + /// Index of the transport that is for. + /// This is primarily used when supporting multiple transports. + /// + public int TransportIndex; + /// + /// New connection state. + /// + public LocalConnectionState ConnectionState; + + public ServerConnectionStateArgs(LocalConnectionState connectionState, int transportIndex) + { + ConnectionState = connectionState; + TransportIndex = transportIndex; + } + } + + /// + /// Container about a connection state change for the local client. + /// + public struct ClientConnectionStateArgs + { + /// + /// New connection state. + /// + public LocalConnectionState ConnectionState; + /// + /// Index of the transport that is for. + /// This is primarily used when supporting multiple transports. + /// + public int TransportIndex; + + public ClientConnectionStateArgs(LocalConnectionState connectionState, int transportIndex) + { + ConnectionState = connectionState; + TransportIndex = transportIndex; + } + } +} + diff --git a/Assets/FishNet/Runtime/Transporting/EventStructures.cs.meta b/Assets/FishNet/Runtime/Transporting/EventStructures.cs.meta new file mode 100644 index 0000000..3113c88 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/EventStructures.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 718b9d27800e70848b50b2c7b0117e5c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/IPAddressType.cs b/Assets/FishNet/Runtime/Transporting/IPAddressType.cs new file mode 100644 index 0000000..0cc079e --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/IPAddressType.cs @@ -0,0 +1,19 @@ +namespace FishNet.Transporting +{ + /// + /// Channel which data is sent or received. + /// + public enum IPAddressType : byte + { + /// + /// Address is IPv4. + /// + IPv4 = 0, + /// + /// Address is IPv6. + /// + IPv6 = 1 + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Transporting/IPAddressType.cs.meta b/Assets/FishNet/Runtime/Transporting/IPAddressType.cs.meta new file mode 100644 index 0000000..4522f21 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/IPAddressType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 11a2c7610ce4ce34a915683bd4607714 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/NetworkReaderLoop.cs b/Assets/FishNet/Runtime/Transporting/NetworkReaderLoop.cs new file mode 100644 index 0000000..e5953b4 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/NetworkReaderLoop.cs @@ -0,0 +1,33 @@ +using FishNet.Managing.Timing; +using UnityEngine; + +namespace FishNet.Transporting +{ + [DisallowMultipleComponent] + [DefaultExecutionOrder(short.MinValue)] + internal class NetworkReaderLoop : MonoBehaviour + { + #region Private. + /// + /// TimeManager this loop is for. + /// + private TimeManager _timeManager; + #endregion + + private void Awake() + { + _timeManager = GetComponent(); + } + + private void FixedUpdate() + { + _timeManager.TickFixedUpdate(); + } + private void Update() + { + _timeManager.TickUpdate(); + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Transporting/NetworkReaderLoop.cs.meta b/Assets/FishNet/Runtime/Transporting/NetworkReaderLoop.cs.meta new file mode 100644 index 0000000..f4ed745 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/NetworkReaderLoop.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0d359d6ef33641f41a2ae67d1abdfdd3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/NetworkWriterLoop.cs b/Assets/FishNet/Runtime/Transporting/NetworkWriterLoop.cs new file mode 100644 index 0000000..634ee44 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/NetworkWriterLoop.cs @@ -0,0 +1,38 @@ +using FishNet.Managing.Timing; +using UnityEngine; + +namespace FishNet.Transporting +{ + [DisallowMultipleComponent] + [DefaultExecutionOrder(short.MaxValue)] + internal class NetworkWriterLoop : MonoBehaviour + { + #region Private. + /// + /// TimeManager this loop is for. + /// + private TimeManager _timeManager; + #endregion + + private void Awake() + { + _timeManager = GetComponent(); + } + + private void LateUpdate() + { + Iterate(); + } + + /// + /// Performs read on transport. + /// + private void Iterate() + { + _timeManager.TickLateUpdate(); + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Transporting/NetworkWriterLoop.cs.meta b/Assets/FishNet/Runtime/Transporting/NetworkWriterLoop.cs.meta new file mode 100644 index 0000000..3038d73 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/NetworkWriterLoop.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2fed05d526ab23949bac6cd2bf041c35 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/PacketId.cs b/Assets/FishNet/Runtime/Transporting/PacketId.cs new file mode 100644 index 0000000..cfb818a --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/PacketId.cs @@ -0,0 +1,33 @@ +using FishNet.Documenting; + +namespace FishNet.Transporting +{ + + /// + /// PacketIds to indicate the type of packet which is being sent or arriving. + /// + [APIExclude] + public enum PacketId : ushort + { + Unset = 0, + Authenticated = 1, + Split = 2, + ObjectSpawn = 3, + ObjectDespawn = 4, + PredictedSpawnResult = 5, + SyncVar = 7, + ServerRpc = 8, + ObserversRpc = 9, + TargetRpc = 10, + OwnershipChange = 11, + Broadcast = 12, + SyncObject = 13, + PingPong = 14, + Replicate = 15, + Reconcile = 16, + Disconnect = 17, + TimingUpdate = 18, + NetworkLODUpdate = 19, + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Transporting/PacketId.cs.meta b/Assets/FishNet/Runtime/Transporting/PacketId.cs.meta new file mode 100644 index 0000000..6fa4a88 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/PacketId.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3f3b7256982245b46a2925e2b94ce149 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transport.cs b/Assets/FishNet/Runtime/Transporting/Transport.cs new file mode 100644 index 0000000..db012f2 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transport.cs @@ -0,0 +1,243 @@ +using FishNet.Managing; +using FishNet.Managing.Logging; +using System; +using UnityEngine; + +namespace FishNet.Transporting +{ + + /// + /// Processes connection states, and data sent to and from a socket. + /// + public abstract class Transport : MonoBehaviour + { + #region Private. + /// + /// NetworkManager for this transport. + /// + public NetworkManager NetworkManager { get; private set; } + /// + /// Index this transport belongs to when using multiple transports at once. + /// + public int Index { get; private set; } + #endregion + + #region Initialization and unity. + /// + /// Initializes the transport. Use this instead of Awake. + /// Index this transport belongs to when using multiple transports at once. + /// + public virtual void Initialize(NetworkManager networkManager, int transportIndex) + { + NetworkManager = networkManager; + Index = transportIndex; + } + #endregion + + #region ConnectionStates. + /// + /// Gets the address of a remote connection Id. + /// + /// Connectionid to get the address for. + /// + public abstract string GetConnectionAddress(int connectionId); + /// + /// Called when a connection state changes for the local client. + /// + public abstract event Action OnClientConnectionState; + /// + /// Called when a connection state changes for the local server. + /// + public abstract event Action OnServerConnectionState; + /// + /// Called when a connection state changes for a remote client. + /// + public abstract event Action OnRemoteConnectionState; + /// + /// Handles a ConnectionStateArgs for the local client. + /// + /// Data being handled. + public abstract void HandleClientConnectionState(ClientConnectionStateArgs connectionStateArgs); + /// + /// Handles a ConnectionStateArgs for the local server. + /// + /// Data being handled. + public abstract void HandleServerConnectionState(ServerConnectionStateArgs connectionStateArgs); + /// + /// Handles a ConnectionStateArgs for a remote client. + /// + /// Data being handled. + public abstract void HandleRemoteConnectionState(RemoteConnectionStateArgs connectionStateArgs); + /// + /// Gets the current local ConnectionState. + /// + /// True if getting ConnectionState for the server. + public abstract LocalConnectionState GetConnectionState(bool server); + /// + /// Gets the current ConnectionState of a client connected to the server. Can only be called on the server. + /// + /// ConnectionId to get ConnectionState for. + public abstract RemoteConnectionState GetConnectionState(int connectionId); + #endregion + + #region Sending. + /// + /// Sends to the server. + /// + /// Channel to use. + /// Data to send. + public abstract void SendToServer(byte channelId, ArraySegment segment); + /// + /// Sends to a client. + /// + /// Channel to use. + /// Data to send. + /// ConnectionId to send to. When sending to clients can be used to specify which connection to send to. + public abstract void SendToClient(byte channelId, ArraySegment segment, int connectionId); + #endregion + + #region Receiving + /// + /// Called when the client receives data. + /// + public abstract event Action OnClientReceivedData; + /// + /// Handles a ClientReceivedDataArgs. + /// + /// Data being handled. + public abstract void HandleClientReceivedDataArgs(ClientReceivedDataArgs receivedDataArgs); + /// + /// Called when the server receives data. + /// + public abstract event Action OnServerReceivedData; + /// + /// Handles a ServerReceivedDataArgs. + /// + /// Data being handled. + public abstract void HandleServerReceivedDataArgs(ServerReceivedDataArgs receivedDataArgs); + #endregion + + #region Iterating. + /// + /// Processes data received by the socket. + /// + /// True to process data received on the server. + public abstract void IterateIncoming(bool server); + /// + /// Processes data to be sent by the socket. + /// + /// True to process data received on the server. + public abstract void IterateOutgoing(bool server); + #endregion + + #region Configuration. + /// + /// Returns if the transport is only run locally, offline. + /// While true several security checks are disabled. + /// + /// + public virtual bool IsLocalTransport(int connectionid) => false; + /// + /// Gets how long in seconds until either the server or client socket must go without data before being timed out. + /// + /// True to get the timeout for the server socket, false for the client socket. + /// + public virtual float GetTimeout(bool asServer) => -1f; + /// + /// Sets how long in seconds until either the server or client socket must go without data before being timed out. + /// + /// True to set the timeout for the server socket, false for the client socket. + public virtual void SetTimeout(float value, bool asServer) { } + /// + /// Returns the maximum number of clients allowed to connect to the server. If the transport does not support this method the value -1 is returned. + /// + /// Maximum clients transport allows. + public virtual int GetMaximumClients() + { + string message = $"The current transport does not support this feature."; + if (NetworkManager == null) + NetworkManager.StaticLogWarning(message); + else + NetworkManager.LogWarning(message); + + return -1; + } + /// + /// Sets the maximum number of clients allowed to connect to the server. If applied at runtime and clients exceed this value existing clients will stay connected but new clients may not connect. + /// + /// Maximum clients to allow. + public virtual void SetMaximumClients(int value) + { + string message = $"The current transport does not support this feature."; + if (NetworkManager == null) + NetworkManager.StaticLogWarning(message); + else + NetworkManager.LogWarning(message); + } + /// + /// Sets which address the client will connect to. + /// + /// Address client will connect to. + public virtual void SetClientAddress(string address) { } + /// + /// Returns which address the client will connect to. + /// + public virtual string GetClientAddress() => string.Empty; + /// + /// Sets which address the server will bind to. + /// + /// Address server will bind to. + /// Address type to set. + public virtual void SetServerBindAddress(string address, IPAddressType addressType) { } + /// + /// Gets which address the server will bind to. + /// + /// Address type to return. + public virtual string GetServerBindAddress(IPAddressType addressType) => string.Empty; + /// + /// Sets which port to use. + /// + /// Port to use. + public virtual void SetPort(ushort port) { } + /// + /// Gets which port to use. + /// + public virtual ushort GetPort() => 0; + #endregion + + #region Start and stop. + /// + /// Starts the local server or client using configured settings. + /// + /// True to start server. + public abstract bool StartConnection(bool server); + /// + /// Stops the local server or client. + /// + /// True to stop server. + public abstract bool StopConnection(bool server); + /// + /// Stops a remote client from the server, disconnecting the client. + /// + /// ConnectionId of the client to disconnect. + /// True to abrutly stop the client socket. The technique used to accomplish immediate disconnects may vary depending on the transport. + /// When not using immediate disconnects it's recommended to perform disconnects using the ServerManager rather than accessing the transport directly. + /// + public abstract bool StopConnection(int connectionId, bool immediately); + /// + /// Stops both client and server. + /// + public abstract void Shutdown(); + #endregion + + #region Channels. + /// + /// Gets the MTU for a channel. + /// + /// Channel to get MTU for. + /// MTU of channel. + public abstract int GetMTU(byte channel); + #endregion + + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transport.cs.meta b/Assets/FishNet/Runtime/Transporting/Transport.cs.meta new file mode 100644 index 0000000..4f1ee6f --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transport.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 78aba14618b37ea4bb067fa95ede84e0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports.meta b/Assets/FishNet/Runtime/Transporting/Transports.meta new file mode 100644 index 0000000..a0f770e --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bb8b443596223e84aaca2634238adda3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Multipass.meta b/Assets/FishNet/Runtime/Transporting/Transports/Multipass.meta new file mode 100644 index 0000000..421c7c8 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Multipass.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7f35b43a13fceaa40ac25cef58d8e53b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Multipass/CHANGELOG.txt b/Assets/FishNet/Runtime/Transporting/Transports/Multipass/CHANGELOG.txt new file mode 100644 index 0000000..4f3eea1 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Multipass/CHANGELOG.txt @@ -0,0 +1,2 @@ +1.0.0 + - Initial release. \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Multipass/CHANGELOG.txt.meta b/Assets/FishNet/Runtime/Transporting/Transports/Multipass/CHANGELOG.txt.meta new file mode 100644 index 0000000..8a99e4e --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Multipass/CHANGELOG.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9b339dd67a0ce7f458236a3ad1d97322 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Multipass/Multipass.cs b/Assets/FishNet/Runtime/Transporting/Transports/Multipass/Multipass.cs new file mode 100644 index 0000000..1db39f2 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Multipass/Multipass.cs @@ -0,0 +1,946 @@ +using FishNet.Managing; +using FishNet.Utility.Extension; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Transporting.Multipass +{ + [AddComponentMenu("FishNet/Transport/Multipass")] + public class Multipass : Transport + { + #region Types. + public struct TransportIdData + { + public int TransportId; + public int TransportIndex; + + public TransportIdData(int transportId, int transportIndex) + { + TransportId = transportId; + TransportIndex = transportIndex; + } + } + #endregion + + #region Public. + /// + /// While true server actions such as starting or stopping the server will run on all transport. + /// + [Tooltip("While true server actions such as starting or stopping the server will run on all transport.")] + public bool GlobalServerActions = true; + /// + /// + /// + private Transport _clientTransport; + /// + /// Transport the client is using. + /// Use SetClientTransport to assign this value. + /// + [HideInInspector] + public Transport ClientTransport + { + get + { + //If not yet set. + if (_clientTransport == null) + { + //If there are transports to set from. + if (_transports.Count != 0) + _clientTransport = _transports[0]; + + /* Give feedback to developer that transport was not set + * before accessing this. Transport should always be set + * manually rather than assuming the default client + * transport. */ + if (_clientTransport == null) + base.NetworkManager.LogError($"ClientTransport in Multipass could not be set to the first transport. This can occur if no trnasports are specified or if the first entry is null."); + else + base.NetworkManager.LogError($"ClientTransport in Multipass is being automatically set to {_clientTransport.GetType()}. For production use SetClientTransport before attempting to access the ClientTransport."); + } + + return _clientTransport; + } + + private set => _clientTransport = value; + } + #endregion + + #region Serialized. + /// + /// + /// + [Tooltip("Transports to use.")] + [SerializeField] + private List _transports = new List(); + /// + /// Transports to use. + /// + public IList Transports => _transports; + #endregion + + #region Private. + /// + /// Key is the transport connectionid, Value is the TransportIdData. + /// + private Dictionary _multipassToTransport = new Dictionary(); + /// + /// Key is the Multipass connectionId, Value is the transport connectionId. + /// + private List> _transportToMultipass = new List>(); + /// + /// Ids available to new connections. + /// + private Queue _availableIds = new Queue(); + #endregion + + #region Const. + /// + /// Id to use for client when acting as host. + /// + internal const int CLIENT_HOST_ID = short.MaxValue; + #endregion + + public override void Initialize(NetworkManager networkManager, int transportIndex) + { + base.Initialize(networkManager, transportIndex); + + //Remove any null transports and warn. + for (int i = 0; i < _transports.Count; i++) + { + if (_transports[i] == null) + { + base.NetworkManager.LogWarning($"Transports contains a null entry on index {i}."); + _transports.RemoveAt(i); + i--; + } + } + + //No transports to use. + if (_transports.Count == 0) + { + base.NetworkManager.LogError($"No transports are set within Multipass."); + return; + } + + //Create transportsToMultipass. + for (int i = 0; i < _transports.Count; i++) + { + Dictionary dict = new Dictionary(); + _transportToMultipass.Add(dict); + } + + //Initialize each transport. + for (int i = 0; i < _transports.Count; i++) + { + _transports[i].Initialize(networkManager, i); + _transports[i].OnClientConnectionState += Multipass_OnClientConnectionState; + _transports[i].OnServerConnectionState += Multipass_OnServerConnectionState; + _transports[i].OnRemoteConnectionState += Multipass_OnRemoteConnectionState; + _transports[i].OnClientReceivedData += Multipass_OnClientReceivedData; + _transports[i].OnServerReceivedData += Multipass_OnServerReceivedData; + } + } + + private void OnDestroy() + { + //Initialize each transport. + foreach (Transport t in _transports) + t.Shutdown(); + } + + + #region ClientIds. + /// + /// Clears ClientIds when appropriate. + /// + private void TryResetClientIds(bool force) + { + //Can only clear when every transport server isnt connected. + if (!force) + { + foreach (Transport t in _transports) + { + //Cannot clear if a server is running still. + if (t.GetConnectionState(true) == LocalConnectionState.Started) + return; + } + } + + _multipassToTransport.Clear(); + foreach (Dictionary item in _transportToMultipass) + item.Clear(); + CreateAvailableIds(); + } + + /// + /// Gets the Multipass connectionId using a transport connectionid. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool GetMultipassId(int transportIndex, int transportId, out int multipassId) + { + Dictionary dict = _transportToMultipass[transportIndex]; + if (!dict.TryGetValueIL2CPP(transportId, out multipassId)) + { + multipassId = -1; + base.NetworkManager.LogError($"Multipass connectionId could not be found for transportIndex {transportIndex}, transportId of {transportId}."); + + return false; + } + + return true; + } + + /// + /// Gets the TransportIdData using a Multipass connectionId. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool GetTransportIdData(int multipassId, out TransportIdData data) + { + if (!_multipassToTransport.TryGetValueIL2CPP(multipassId, out data)) + { + //Fall through. + base.NetworkManager.LogError($"TransportIdData could not be found for Multipass connectionId of {multipassId}."); + return false; + } + + return true; + } + #endregion + + #region ConnectionStates. + /// + /// Gets the IP address of a remote connectionId. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override string GetConnectionAddress(int connectionId) + { + TransportIdData data; + if (!GetTransportIdData(connectionId, out data)) + return string.Empty; + + return _transports[data.TransportIndex].GetConnectionAddress(data.TransportId); + } + /// + /// Called when a connection state changes for the local client. + /// + public override event Action OnClientConnectionState; + /// + /// Called when a connection state changes for the local server. + /// + public override event Action OnServerConnectionState; + /// + /// Called when a connection state changes for a remote client. + /// + public override event Action OnRemoteConnectionState; + /// + /// Gets the current local ConnectionState of the first transport. + /// + /// True if getting ConnectionState for the server. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override LocalConnectionState GetConnectionState(bool server) + { + if (server) + { + base.NetworkManager.LogError($"This method is not supported for server. Use GetConnectionState(server, transportIndex) instead."); + return LocalConnectionState.Stopped; + } + + if (IsClientTransportSetWithError("GetConnectionState")) + return GetConnectionState(server, ClientTransport.Index); + else + return LocalConnectionState.Stopped; + } + /// + /// Gets the current local ConnectionState of the transport on index. + /// + /// True if getting ConnectionState for the server. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public LocalConnectionState GetConnectionState(bool server, int index) + { + if (!IndexInRange(index, true)) + return LocalConnectionState.Stopped; + + return _transports[index].GetConnectionState(server); + } + /// + /// Gets the current ConnectionState of a remote client on the server. + /// + /// ConnectionId to get ConnectionState for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override RemoteConnectionState GetConnectionState(int connectionId) + { + + TransportIdData data; + if (!GetTransportIdData(connectionId, out data)) + return RemoteConnectionState.Stopped; + + return _transports[data.TransportIndex].GetConnectionState(data.TransportId); + } + /// + /// Gets the current ConnectionState of a remote client on the server of the transport on index. + /// + /// ConnectionId to get ConnectionState for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public RemoteConnectionState GetConnectionState(int connectionId, int index) + { + if (!IndexInRange(index, true)) + return RemoteConnectionState.Stopped; + + return _transports[index].GetConnectionState(connectionId); + } + + /// + /// Handles a ConnectionStateArgs for the local client. + /// + /// + private void Multipass_OnClientConnectionState(ClientConnectionStateArgs connectionStateArgs) + { + OnClientConnectionState?.Invoke(connectionStateArgs); + } + /// + /// Handles a ConnectionStateArgs for the local server. + /// + /// + private void Multipass_OnServerConnectionState(ServerConnectionStateArgs connectionStateArgs) + { + OnServerConnectionState?.Invoke(connectionStateArgs); + TryResetClientIds(false); + } + /// + /// Handles a ConnectionStateArgs for a remote client. + /// + /// + private void Multipass_OnRemoteConnectionState(RemoteConnectionStateArgs connectionStateArgs) + { + /* When starting Multipass needs to get a new + * connectionId to be used within FN. This is the 'ClientId' + * that is passed around for ownership, rpcs, ect. + * + * The new connectionId will be linked with the connectionId + * from the transport, named transportConnectionid. + * + * When data arrives the transportStateId is used as a key + * in fromClientIds, where Multipass Id is returned. The argument values + * are then overwritten with the MultipassId. + * + * When data is being sent the same process is performed but reversed. + * The connectionId is looked up in toClientIds, where the transportConnectionId + * is output. Then as before the argument values are overwritten with the + * transportConnectionId. */ + + int transportIndex = connectionStateArgs.TransportIndex; + int transportId = connectionStateArgs.ConnectionId; + int multipassId; + Dictionary transportToMultipass = _transportToMultipass[transportIndex]; + + //Started. + if (connectionStateArgs.ConnectionState == RemoteConnectionState.Started) + { + multipassId = _availableIds.Dequeue(); + transportToMultipass[transportId] = multipassId; + _multipassToTransport[multipassId] = new TransportIdData(transportId, transportIndex); + } + //Stopped. + else + { + if (!GetMultipassId(transportIndex, transportId, out multipassId)) + return; + + _availableIds.Enqueue(multipassId); + transportToMultipass.Remove(transportId); + _multipassToTransport.Remove(multipassId); + +#if UNITY_EDITOR || DEVELOPMENT_BUILD + //Remove packets held for connection from latency simulator. + base.NetworkManager.TransportManager.LatencySimulator.RemovePendingForConnection(multipassId); +#endif + } + + connectionStateArgs.ConnectionId = multipassId; + OnRemoteConnectionState?.Invoke(connectionStateArgs); + } + #endregion + + #region Iterating. + /// + /// Processes data received by the socket. + /// + /// True to process data received on the server. + public override void IterateIncoming(bool server) + { + foreach (Transport t in _transports) + t.IterateIncoming(server); + } + + /// + /// Processes data to be sent by the socket. + /// + /// True to process data received on the server. + public override void IterateOutgoing(bool server) + { + foreach (Transport t in _transports) + t.IterateOutgoing(server); + } + #endregion + + #region ReceivedData. + /// + /// Called when client receives data. + /// + public override event Action OnClientReceivedData; + /// + /// Handles a ClientReceivedDataArgs. + /// + /// + private void Multipass_OnClientReceivedData(ClientReceivedDataArgs receivedDataArgs) + { + OnClientReceivedData?.Invoke(receivedDataArgs); + } + /// + /// Called when server receives data. + /// + public override event Action OnServerReceivedData; + /// + /// Handles a ClientReceivedDataArgs. + /// + /// + private void Multipass_OnServerReceivedData(ServerReceivedDataArgs receivedDataArgs) + { + int multipassId; + if (!GetMultipassId(receivedDataArgs.TransportIndex, receivedDataArgs.ConnectionId, out multipassId)) + return; + + receivedDataArgs.ConnectionId = multipassId; + OnServerReceivedData?.Invoke(receivedDataArgs); + } + #endregion + + #region Sending. + /// + /// Sends to the server on ClientTransport. + /// + /// Channel to use. + /// /// Data to send. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void SendToServer(byte channelId, ArraySegment segment) + { + if (ClientTransport != null) + ClientTransport.SendToServer(channelId, segment); + } + /// + /// Sends data to a client. + /// + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void SendToClient(byte channelId, ArraySegment segment, int connectionId) + { + TransportIdData data; + if (GetTransportIdData(connectionId, out data)) + _transports[data.TransportIndex].SendToClient(channelId, segment, data.TransportId); + } + #endregion + + #region Configuration. + /// + /// Returns if GlobalServerActions is true and if not logs an error. + /// + /// + private bool UseGlobalServerActionsWithError(string methodText) + { + if (!GlobalServerActions) + { + base.NetworkManager.LogError($"Method {methodText} is not supported while GlobalServerActions is false."); + return false; + } + else + { + return true; + } + } + + /// + /// Returns if ClientTransport is set and if not logs an error. + /// + /// + /// + private bool IsClientTransportSetWithError(string methodText) + { + if (ClientTransport == null) + { + base.NetworkManager.LogError($"ClientTransport is not set. Use SetClientTransport before calling {methodText}."); + return false; + } + else + { + return true; + } + } + /// + /// Populates the availableIds collection. + /// + private void CreateAvailableIds() + { + _availableIds.Clear(); + for (int i = 0; i < short.MaxValue; i++) + _availableIds.Enqueue(i); + } + + /// + /// Sets the client transport to the first of type. + /// + /// + public void SetClientTransport() + { + int index = -1; + for (int i = 0; i < _transports.Count; i++) + { + if (_transports[i].GetType() == typeof(T)) + { + index = i; + break; + } + } + + SetClientTransport(index); + } + + /// + /// Sets the client transport to the first of type T. + /// + /// + public void SetClientTransport(Type type) + { + int index = -1; + for (int i = 0; i < _transports.Count; i++) + { + if (_transports[i].GetType() == type) + { + index = i; + break; + } + } + + SetClientTransport(index); + } + /// + /// Sets the client transport to the matching reference of transport. + /// + /// + public void SetClientTransport(Transport transport) + { + int index = -1; + for (int i = 0; i < _transports.Count; i++) + { + if (_transports[i] == transport) + { + index = i; + break; + } + } + + SetClientTransport(index); + } + /// + /// Sets the client transport to the transport on index. + /// + /// + public void SetClientTransport(int index) + { + if (!IndexInRange(index, true)) + return; + + ClientTransport = _transports[index]; + } + /// + /// Gets the Transport on index. + /// + /// + /// + public Transport GetTransport(int index) + { + if (!IndexInRange(index, true)) + return null; + + return _transports[index]; + } + /// + /// Gets the Transport on of type T. + /// + /// + /// + public T GetTransport() + { + foreach (Transport t in _transports) + { + if (t.GetType() == typeof(T)) + return (T)(object)t; + } + + return default(T); + } + /// + /// Returns if the transport for connectionId is a local transport. + /// While true several security checks are disabled. + /// + public override bool IsLocalTransport(int connectionid) + { + //If able to get transport data return value from transport. + if (GetTransportIdData(connectionid, out TransportIdData data)) + return _transports[data.TransportIndex].IsLocalTransport(data.TransportId); + //Otherwise return false forcing checks. + else + return false; + } + + /// + /// Returns the maximum number of clients allowed to connect to the server. If the transport does not support this method the value -1 is returned. + /// The first transport is used. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetMaximumClients() + { + base.NetworkManager.LogError($"This method is not supported. Use GetMaximumClients(transportIndex) instead."); + + return -1; + } + /// + /// Returns the maximum number of clients allowed to connect to the server. If the transport does not support this method the value -1 is returned. + /// The first transport is used. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetMaximumClients(int transportIndex) + { + if (!IndexInRange(transportIndex, true)) + return -1; + + return _transports[transportIndex].GetMaximumClients(); + } + /// + /// Sets maximum number of clients allowed to connect to the server. If applied at runtime and clients exceed this value existing clients will stay connected but new clients may not connect. + /// This sets the value to the transport on the first index. + /// + /// + public override void SetMaximumClients(int value) + { + base.NetworkManager.LogError($"This method is not supported. Use SetMaximumClients(value, transportIndex) instead."); + } + /// + /// Sets maximum number of clients allowed to connect to the server. If applied at runtime and clients exceed this value existing clients will stay connected but new clients may not connect. + /// This sets the value to the transport on index. + /// + /// + public void SetMaximumClients(int value, int transportIndex) + { + if (!IndexInRange(transportIndex, true)) + return; + + _transports[transportIndex].SetMaximumClients(value); + } + /// + /// Sets which address the client will connect to. + /// This will set the address for every transport. + /// + /// + public override void SetClientAddress(string address) + { + foreach (Transport t in _transports) + t.SetClientAddress(address); + } + public override void SetServerBindAddress(string address, IPAddressType addressType) + { + base.NetworkManager.LogError($"This method is not supported. Use SetServerBindAddress(address, transportIndex) instead."); + } + /// Sets which address the server will bind to. + /// This is called on the transport of index. + /// + /// + public void SetServerBindAddress(string address, IPAddressType addressType, int index) + { + if (!IndexInRange(index, true)) + return; + + _transports[index].SetServerBindAddress(address, addressType); + } + /// + /// Sets which port to use on the first transport. + /// + public override void SetPort(ushort port) + { + base.NetworkManager.LogError($"This method is not supported. Use SetPort(port, transportIndex) instead."); + } + /// + /// Sets which port to use on transport of index. + /// + public void SetPort(ushort port, int index) + { + if (!IndexInRange(index, true)) + return; + + _transports[index].SetPort(port); + } + #endregion + + #region Start and stop. + /// + /// Starts the local server or client using configured settings on the first transport. + /// + /// True to start server. + public override bool StartConnection(bool server) + { + //Server. + if (server) + { + if (!UseGlobalServerActionsWithError("StartConnection")) + return false; + + bool success = true; + for (int i = 0; i < _transports.Count; i++) + { + if (!StartConnection(true, i)) + success = false; + } + + return success; + } + //Client. + else + { + if (IsClientTransportSetWithError("StartConnection")) + return StartConnection(false, ClientTransport.Index); + else + return false; + } + } + + /// + /// Starts the local server or client using configured settings on transport of index. + /// + /// True to start server. + public bool StartConnection(bool server, int index) + { + if (server) + { + return StartServer(index); + } + else + { + if (IsClientTransportSetWithError("StartConnection")) + return StartClient(); + else + return false; + } + } + + + /// + /// Stops the local server or client on the first transport. + /// + /// True to stop server. + public override bool StopConnection(bool server) + { + //Server + if (server) + { + if (!UseGlobalServerActionsWithError("StopConnection")) + return false; + + bool success = true; + for (int i = 0; i < _transports.Count; i++) + { + if (!StopConnection(true, i)) + success = false; + } + + return success; + } + //Client. + else + { + if (IsClientTransportSetWithError("StopConnection")) + return StopConnection(false, ClientTransport.Index); + else + return false; + } + } + /// + /// Stops the local server or client on transport of index. + /// + /// True to stop server. + public bool StopConnection(bool server, int index) + { + if (server) + { + return StopServer(index); + } + else + { + if (IsClientTransportSetWithError("StopConnection")) + return StopClient(); + else + return false; + } + } + + /// + /// Stops a remote client from the server, disconnecting the client. + /// + /// ConnectionId of the client to disconnect. + /// True to abrutly stp the client socket without waiting socket thread. + public override bool StopConnection(int connectionId, bool immediately) + { + return StopClient(connectionId, immediately); + } + + /// + /// Stops the server connection on transportIndex. + /// + /// True to send a disconnect message to connections before stopping them. + /// Index of transport to stop on. + public bool StopServerConnection(bool sendDisconnectMessage, int transportIndex) + { + if (sendDisconnectMessage) + { + //Get connectionIds as ServerManager knows them. + int[] multipassIds = _transportToMultipass[transportIndex].Keys.ToArray(); + //Tell serve manager to write disconnect for those ids. + base.NetworkManager.ServerManager.SendDisconnectMessages(multipassIds); + //Iterate outgoing on transport which is being stopped. + _transports[transportIndex].IterateOutgoing(true); + } + + return StopConnection(true, transportIndex); + } + + /// + /// Stops both client and server on all transports. + /// + public override void Shutdown() + { + foreach (Transport t in _transports) + { + //Stops client then server connections. + t.StopConnection(false); + t.StopConnection(true); + } + } + + #region Privates. + /// + /// Starts server of transport on index. + /// + /// True if there were no blocks. A true response does not promise a socket will or has connected. + private bool StartServer(int index) + { + if (!IndexInRange(index, true)) + return false; + + return _transports[index].StartConnection(true); + } + + /// + /// Stops server of transport on index. + /// + private bool StopServer(int index) + { + if (!IndexInRange(index, true)) + return false; + + return _transports[index].StopConnection(true); + } + + /// + /// Starts the client on ClientTransport. + /// + /// + /// True if there were no blocks. A true response does not promise a socket will or has connected. + private bool StartClient() + { + return ClientTransport.StartConnection(false); + } + + /// + /// Stops the client on ClientTransport. + /// + private bool StopClient() + { + return ClientTransport.StopConnection(false); + } + + /// + /// Stops a remote client on the server. + /// + /// + /// True to abrutly stp the client socket without waiting socket thread. + private bool StopClient(int connectionId, bool immediately) + { + TransportIdData data; + if (!GetTransportIdData(connectionId, out data)) + return false; + + return _transports[data.TransportIndex].StopConnection(data.TransportId, immediately); + } + #endregion + #endregion + + #region Channels. + /// + /// Gets the MTU for a channel on the first transport. This should take header size into consideration. + /// For example, if MTU is 1200 and a packet header for this channel is 10 in size, this method should return 1190. + /// + /// + /// + public override int GetMTU(byte channel) + { + return GetMTU(channel, 0); + } + /// + /// Gets the MTU for a channel of transport on index. This should take header size into consideration. + /// For example, if MTU is 1200 and a packet header for this channel is 10 in size, this method should return 1190. + /// + /// + /// + public int GetMTU(byte channel, int index) + { + if (!IndexInRange(index, true)) + return -1; + + return _transports[index].GetMTU(channel); + } + + #endregion + + #region Misc. + /// + /// Returns if an index is within range of the Transports collection. + /// + private bool IndexInRange(int index, bool error) + { + if (index >= _transports.Count || index < 0) + { + if (error) + base.NetworkManager.LogError($"Index of {index} is out of Transports range."); + return false; + } + else + { + return true; + } + } + + //perf change events to direct calls in transports. + public override void HandleServerConnectionState(ServerConnectionStateArgs connectionStateArgs) { } + public override void HandleRemoteConnectionState(RemoteConnectionStateArgs connectionStateArgs) { } + public override void HandleClientReceivedDataArgs(ClientReceivedDataArgs receivedDataArgs) { } + public override void HandleServerReceivedDataArgs(ServerReceivedDataArgs receivedDataArgs) { } + public override void HandleClientConnectionState(ClientConnectionStateArgs connectionStateArgs) { } + #endregion + + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Multipass/Multipass.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Multipass/Multipass.cs.meta new file mode 100644 index 0000000..f58b9fb --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Multipass/Multipass.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 314b449d3505bd24487ba69b61c2fda5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Multipass/VERSION.txt b/Assets/FishNet/Runtime/Transporting/Transports/Multipass/VERSION.txt new file mode 100644 index 0000000..afaf360 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Multipass/VERSION.txt @@ -0,0 +1 @@ +1.0.0 \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Multipass/VERSION.txt.meta b/Assets/FishNet/Runtime/Transporting/Transports/Multipass/VERSION.txt.meta new file mode 100644 index 0000000..7333afa --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Multipass/VERSION.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e4f9d944e2ca8484587859cf4ec80b6c +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat.meta new file mode 100644 index 0000000..eeec7bc --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 43760836a07366846a82fe7f158bd84e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core.meta new file mode 100644 index 0000000..1ad2510 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4dad76b44081bb54b97e6da2e0e6f26d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ClientSocket.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ClientSocket.cs new file mode 100644 index 0000000..7c945e9 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ClientSocket.cs @@ -0,0 +1,307 @@ +using FishNet.Managing.Logging; +using LiteNetLib; +using LiteNetLib.Layers; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using UnityEngine; + +namespace FishNet.Transporting.Tugboat.Client +{ + public class ClientSocket : CommonSocket + { + ~ClientSocket() + { + StopConnection(); + } + + #region Private. + #region Configuration. + /// + /// Address to bind server to. + /// + private string _address = string.Empty; + /// + /// Port used by server. + /// + private ushort _port; + /// + /// MTU sizes for each channel. + /// + private int _mtu; + #endregion + #region Queues. + /// + /// Changes to the sockets local connection state. + /// + private Queue _localConnectionStates = new Queue(); + /// + /// Inbound messages which need to be handled. + /// + private Queue _incoming = new Queue(); + /// + /// Outbound messages which need to be handled. + /// + private Queue _outgoing = new Queue(); + #endregion + /// + /// Client socket manager. + /// + private NetManager _client; + /// + /// How long in seconds until client times from server. + /// + private int _timeout; + /// + /// PacketLayer to use with LiteNetLib. + /// + private PacketLayerBase _packetLayer; + /// + /// Locks the NetManager to stop it. + /// + private readonly object _stopLock = new object(); + #endregion + + /// + /// Initializes this for use. + /// + /// + internal void Initialize(Transport t, int unreliableMTU, PacketLayerBase packetLayer) + { + base.Transport = t; + _mtu = unreliableMTU; + _packetLayer = packetLayer; + } + + /// + /// Updates the Timeout value as seconds. + /// + internal void UpdateTimeout(int timeout) + { + _timeout = timeout; + base.UpdateTimeout(_client, timeout); + } + + /// + /// Threaded operation to process client actions. + /// + private void ThreadedSocket() + { + EventBasedNetListener listener = new EventBasedNetListener(); + listener.NetworkReceiveEvent += Listener_NetworkReceiveEvent; + listener.PeerConnectedEvent += Listener_PeerConnectedEvent; + listener.PeerDisconnectedEvent += Listener_PeerDisconnectedEvent; + + _client = new NetManager(listener, _packetLayer); + _client.MtuOverride = (_mtu + NetConstants.FragmentedHeaderTotalSize); + + UpdateTimeout(_timeout); + + _localConnectionStates.Enqueue(LocalConnectionState.Starting); + _client.Start(); + _client.Connect(_address, _port, string.Empty); + } + + + /// + /// Stops the socket on a new thread. + /// + private void StopSocketOnThread() + { + if (_client == null) + return; + + Task t = Task.Run(() => + { + lock (_stopLock) + { + _client?.Stop(); + _client = null; + } + + //If not stopped yet also enqueue stop. + if (base.GetConnectionState() != LocalConnectionState.Stopped) + _localConnectionStates.Enqueue(LocalConnectionState.Stopped); + }); + } + + /// + /// Starts the client connection. + /// + /// + /// + /// + /// + internal bool StartConnection(string address, ushort port) + { + if (base.GetConnectionState() != LocalConnectionState.Stopped) + return false; + + base.SetConnectionState(LocalConnectionState.Starting, false); + + //Assign properties. + _port = port; + _address = address; + + ResetQueues(); + Task t = Task.Run(() => ThreadedSocket()); + + return true; + } + + + /// + /// Stops the local socket. + /// + internal bool StopConnection(DisconnectInfo? info = null) + { + if (base.GetConnectionState() == LocalConnectionState.Stopped || base.GetConnectionState() == LocalConnectionState.Stopping) + return false; + + if (info != null) + base.Transport.NetworkManager.Log($"Local client disconnect reason: {info.Value.Reason}."); + + base.SetConnectionState(LocalConnectionState.Stopping, false); + StopSocketOnThread(); + return true; + } + + /// + /// Resets queues. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ResetQueues() + { + _localConnectionStates.Clear(); + base.ClearPacketQueue(ref _incoming); + base.ClearPacketQueue(ref _outgoing); + } + + + /// + /// Called when disconnected from the server. + /// + private void Listener_PeerDisconnectedEvent(NetPeer peer, DisconnectInfo disconnectInfo) + { + StopConnection(disconnectInfo); + } + + /// + /// Called when connected to the server. + /// + private void Listener_PeerConnectedEvent(NetPeer peer) + { + _localConnectionStates.Enqueue(LocalConnectionState.Started); + } + + /// + /// Called when data is received from a peer. + /// + private void Listener_NetworkReceiveEvent(NetPeer fromPeer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod) + { + base.Listener_NetworkReceiveEvent(_incoming, fromPeer, reader, deliveryMethod, _mtu); + } + + /// + /// Dequeues and processes outgoing. + /// + private void DequeueOutgoing() + { + NetPeer peer = null; + if (_client != null) + peer = _client.FirstPeer; + //Server connection hasn't been made. + if (peer == null) + { + /* Only dequeue outgoing because other queues might have + * relevant information, such as the local connection queue. */ + base.ClearPacketQueue(ref _outgoing); + } + else + { + int count = _outgoing.Count; + for (int i = 0; i < count; i++) + { + Packet outgoing = _outgoing.Dequeue(); + + ArraySegment segment = outgoing.GetArraySegment(); + DeliveryMethod dm = (outgoing.Channel == (byte)Channel.Reliable) ? + DeliveryMethod.ReliableOrdered : DeliveryMethod.Unreliable; + + //If over the MTU. + if (outgoing.Channel == (byte)Channel.Unreliable && segment.Count > _mtu) + { + base.Transport.NetworkManager.LogWarning($"Client is sending of {segment.Count} length on the unreliable channel, while the MTU is only {_mtu}. The channel has been changed to reliable for this send."); + dm = DeliveryMethod.ReliableOrdered; + } + + peer.Send(segment.Array, segment.Offset, segment.Count, dm); + + outgoing.Dispose(); + } + } + } + + /// + /// Allows for Outgoing queue to be iterated. + /// + internal void IterateOutgoing() + { + DequeueOutgoing(); + } + + /// + /// Iterates the Incoming queue. + /// + internal void IterateIncoming() + { + _client?.PollEvents(); + + /* Run local connection states first so we can begin + * to read for data at the start of the frame, as that's + * where incoming is read. */ + while (_localConnectionStates.Count > 0) + base.SetConnectionState(_localConnectionStates.Dequeue(), false); + + //Not yet started, cannot continue. + LocalConnectionState localState = base.GetConnectionState(); + if (localState != LocalConnectionState.Started) + { + ResetQueues(); + //If stopped try to kill task. + if (localState == LocalConnectionState.Stopped) + { + StopSocketOnThread(); + return; + } + } + + /* Incoming. */ + while (_incoming.Count > 0) + { + Packet incoming = _incoming.Dequeue(); + ClientReceivedDataArgs dataArgs = new ClientReceivedDataArgs( + incoming.GetArraySegment(), + (Channel)incoming.Channel, base.Transport.Index); + base.Transport.HandleClientReceivedDataArgs(dataArgs); + //Dispose of packet. + incoming.Dispose(); + } + } + + /// + /// Sends a packet to the server. + /// + internal void SendToServer(byte channelId, ArraySegment segment) + { + //Not started, cannot send. + if (base.GetConnectionState() != LocalConnectionState.Started) + return; + + base.Send(ref _outgoing, channelId, segment, -1, _mtu); + } + + + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ClientSocket.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ClientSocket.cs.meta new file mode 100644 index 0000000..989bf71 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ClientSocket.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7944de5e4da77594db036e276174ee60 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/CommonSocket.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/CommonSocket.cs new file mode 100644 index 0000000..cdc77b4 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/CommonSocket.cs @@ -0,0 +1,125 @@ +using FishNet.Utility.Performance; +using LiteNetLib; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; + +namespace FishNet.Transporting.Tugboat +{ + + public abstract class CommonSocket + { + + #region Public. + /// + /// Current ConnectionState. + /// + private LocalConnectionState _connectionState = LocalConnectionState.Stopped; + /// + /// Returns the current ConnectionState. + /// + /// + internal LocalConnectionState GetConnectionState() + { + return _connectionState; + } + /// + /// Sets a new connection state. + /// + /// + protected void SetConnectionState(LocalConnectionState connectionState, bool asServer) + { + //If state hasn't changed. + if (connectionState == _connectionState) + return; + + _connectionState = connectionState; + if (asServer) + Transport.HandleServerConnectionState(new ServerConnectionStateArgs(connectionState, Transport.Index)); + else + Transport.HandleClientConnectionState(new ClientConnectionStateArgs(connectionState, Transport.Index)); + } + #endregion + + #region Protected. + /// + /// Transport controlling this socket. + /// + protected Transport Transport = null; + #endregion + + + /// + /// Sends data to connectionId. + /// + internal void Send(ref Queue queue, byte channelId, ArraySegment segment, int connectionId, int mtu) + { + if (GetConnectionState() != LocalConnectionState.Started) + return; + + //ConnectionId isn't used from client to server. + Packet outgoing = new Packet(connectionId, segment, channelId, mtu); + queue.Enqueue(outgoing); + } + + /// + /// Updates the timeout for NetManager. + /// + protected void UpdateTimeout(NetManager netManager, int timeout) + { + if (netManager == null) + return; + + timeout = (timeout == 0) ? int.MaxValue : Math.Min(int.MaxValue, (timeout * 1000)); + netManager.DisconnectTimeout = timeout; + } + /// + /// Clears a queue using Packet type. + /// + /// + internal void ClearPacketQueue(ref ConcurrentQueue queue) + { + while (queue.TryDequeue(out Packet p)) + p.Dispose(); + } + + /// + /// Clears a queue using Packet type. + /// + /// + internal void ClearPacketQueue(ref Queue queue) + { + int count = queue.Count; + for (int i = 0; i < count; i++) + { + Packet p = queue.Dequeue(); + p.Dispose(); + } + } + + /// + /// Called when data is received. + /// + internal virtual void Listener_NetworkReceiveEvent(Queue queue, NetPeer fromPeer, NetPacketReader reader, DeliveryMethod deliveryMethod, int mtu) + { + //Set buffer. + int dataLen = reader.AvailableBytes; + //Prefer to max out returned array to mtu to reduce chance of resizing. + int arraySize = Math.Max(dataLen, mtu); + byte[] data = ByteArrayPool.Retrieve(arraySize); + reader.GetBytes(data, dataLen); + //Id. + int id = fromPeer.Id; + //Channel. + byte channel = (deliveryMethod == DeliveryMethod.Unreliable) ? + (byte)Channel.Unreliable : (byte)Channel.Reliable; + //Add to packets. + Packet packet = new Packet(id, data, dataLen, channel); + queue.Enqueue(packet); + //Recycle reader. + reader.Recycle(); + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/CommonSocket.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/CommonSocket.cs.meta new file mode 100644 index 0000000..640e1fa --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/CommonSocket.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 329169cdf51866c43a8c42e8aeb291fb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ServerSocket.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ServerSocket.cs new file mode 100644 index 0000000..66adaff --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ServerSocket.cs @@ -0,0 +1,523 @@ +using FishNet.Connection; +using FishNet.Managing.Logging; +using LiteNetLib; +using LiteNetLib.Layers; +using System; +using System.Collections.Generic; +using System.Net; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using UnityEngine; + +namespace FishNet.Transporting.Tugboat.Server +{ + public class ServerSocket : CommonSocket + { + + #region Public. + /// + /// Gets the current ConnectionState of a remote client on the server. + /// + /// ConnectionId to get ConnectionState for. + internal RemoteConnectionState GetConnectionState(int connectionId) + { + NetPeer peer = GetNetPeer(connectionId, false); + if (peer == null || peer.ConnectionState != ConnectionState.Connected) + return RemoteConnectionState.Stopped; + else + return RemoteConnectionState.Started; + } + #endregion + + #region Private. + #region Configuration. + /// + /// Port used by server. + /// + private ushort _port; + /// + /// Maximum number of allowed clients. + /// + private int _maximumClients; + /// + /// MTU size per packet. + /// + private int _mtu; + #endregion + #region Queues. + /// + /// Changes to the sockets local connection state. + /// + private Queue _localConnectionStates = new Queue(); + /// + /// Inbound messages which need to be handled. + /// + private Queue _incoming = new Queue(); + /// + /// Outbound messages which need to be handled. + /// + private Queue _outgoing = new Queue(); + /// + /// ConnectionEvents which need to be handled. + /// + private Queue _remoteConnectionEvents = new Queue(); + #endregion + /// + /// Key required to connect. + /// + private string _key = string.Empty; + /// + /// How long in seconds until client times from server. + /// + private int _timeout; + /// + /// Server socket manager. + /// + private NetManager _server; + /// + /// IPv4 address to bind server to. + /// + private string _ipv4BindAddress; + /// + /// IPv6 address to bind server to. + /// + private string _ipv6BindAddress; + /// + /// PacketLayer to use with LiteNetLib. + /// + private PacketLayerBase _packetLayer; + /// + /// Locks the NetManager to stop it. + /// + private readonly object _stopLock = new object(); + #endregion + + ~ServerSocket() + { + StopConnection(); + } + + /// + /// Initializes this for use. + /// + /// + internal void Initialize(Transport t, int unreliableMTU, PacketLayerBase packetLayer) + { + base.Transport = t; + _mtu = unreliableMTU; + _packetLayer = packetLayer; + } + + /// + /// Updates the Timeout value as seconds. + /// + internal void UpdateTimeout(int timeout) + { + _timeout = timeout; + base.UpdateTimeout(_server, timeout); + } + + + /// + /// Threaded operation to process server actions. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ThreadedSocket() + { + EventBasedNetListener listener = new EventBasedNetListener(); + listener.ConnectionRequestEvent += Listener_ConnectionRequestEvent; + listener.PeerConnectedEvent += Listener_PeerConnectedEvent; + listener.NetworkReceiveEvent += Listener_NetworkReceiveEvent; + listener.PeerDisconnectedEvent += Listener_PeerDisconnectedEvent; + + _server = new NetManager(listener, _packetLayer); + _server.MtuOverride = (_mtu + NetConstants.FragmentedHeaderTotalSize); + + UpdateTimeout(_timeout); + + //Set bind addresses. + IPAddress ipv4; + IPAddress ipv6; + + //Set ipv4 + if (!string.IsNullOrEmpty(_ipv4BindAddress)) + { + if (!IPAddress.TryParse(_ipv4BindAddress, out ipv4)) + ipv4 = null; + + //If unable to parse try to get address another way. + if (ipv4 == null) + { + IPHostEntry hostEntry = Dns.GetHostEntry(_ipv4BindAddress); + if (hostEntry.AddressList.Length > 0) + { + ipv4 = hostEntry.AddressList[0]; + base.Transport.NetworkManager.Log($"IPv4 could not parse correctly but was resolved to {ipv4.ToString()}"); + } + } + } + else + { + IPAddress.TryParse("0.0.0.0", out ipv4); + } + + //Set ipv6. + if (!string.IsNullOrEmpty(_ipv6BindAddress)) + { + if (!IPAddress.TryParse(_ipv6BindAddress, out ipv6)) + ipv6 = null; + } + else + { + IPAddress.TryParse("0:0:0:0:0:0:0:0", out ipv6); + } + + + + string ipv4FailText = (ipv4 == null) ? $"IPv4 address {_ipv4BindAddress} failed to parse. " : string.Empty; + string ipv6FailText = (ipv6 == null) ? $"IPv6 address {_ipv6BindAddress} failed to parse. " : string.Empty; + if (ipv4FailText != string.Empty || ipv6FailText != string.Empty) + { + base.Transport.NetworkManager.Log($"{ipv4FailText}{ipv6FailText}Clear the bind address field to use any bind address."); + StopConnection(); + return; + } + + bool startResult = _server.Start(ipv4, ipv6, _port); + //If started succcessfully. + if (startResult) + { + _localConnectionStates.Enqueue(LocalConnectionState.Started); + } + //Failed to start. + else + { + base.Transport.NetworkManager.LogError($"Server failed to start. This usually occurs when the specified port is unavailable, be it closed or already in use."); + StopConnection(); + } + } + + /// + /// Stops the socket on a new thread. + /// + private void StopSocketOnThread() + { + if (_server == null) + return; + + Task t = Task.Run(() => + { + lock (_stopLock) + { + _server?.Stop(); + _server = null; + } + + //If not stopped yet also enqueue stop. + if (base.GetConnectionState() != LocalConnectionState.Stopped) + _localConnectionStates.Enqueue(LocalConnectionState.Stopped); + }); + } + + /// + /// Gets the address of a remote connection Id. + /// + /// + /// Returns string.empty if Id is not found. + internal string GetConnectionAddress(int connectionId) + { + NetPeer peer = GetNetPeer(connectionId, false); + return peer.EndPoint.Address.ToString(); + } + + /// + /// Returns a NetPeer for connectionId. + /// + /// + /// + private NetPeer GetNetPeer(int connectionId, bool connectedOnly) + { + if (_server != null) + { + NetPeer peer = _server.GetPeerById(connectionId); + if (connectedOnly && peer != null && peer.ConnectionState != ConnectionState.Connected) + peer = null; + + return peer; + } + else + { + return null; + } + } + + /// + /// Starts the server. + /// + internal bool StartConnection(ushort port, int maximumClients, string ipv4BindAddress, string ipv6BindAddress) + { + if (base.GetConnectionState() != LocalConnectionState.Stopped) + return false; + + base.SetConnectionState(LocalConnectionState.Starting, true); + + //Assign properties. + _port = port; + _maximumClients = maximumClients; + _ipv4BindAddress = ipv4BindAddress; + _ipv6BindAddress = ipv6BindAddress; + ResetQueues(); + + Task t = Task.Run(() => ThreadedSocket()); + + return true; + } + + /// + /// Stops the local socket. + /// + internal bool StopConnection() + { + if (_server == null || base.GetConnectionState() == LocalConnectionState.Stopped || base.GetConnectionState() == LocalConnectionState.Stopping) + return false; + + _localConnectionStates.Enqueue(LocalConnectionState.Stopping); + StopSocketOnThread(); + return true; + } + + /// + /// Stops a remote client disconnecting the client from the server. + /// + /// ConnectionId of the client to disconnect. + internal bool StopConnection(int connectionId) + { + //Server isn't running. + if (_server == null || base.GetConnectionState() != LocalConnectionState.Started) + return false; + + NetPeer peer = GetNetPeer(connectionId, false); + if (peer == null) + return false; + + try + { + peer.Disconnect(); + //Let LiteNetLib get the disconnect event which will enqueue a remote connection state. + //base.Transport.HandleRemoteConnectionState(new RemoteConnectionStateArgs(RemoteConnectionState.Stopped, connectionId, base.Transport.Index)); + } + catch + { + return false; + } + + return true; + } + + /// + /// Resets queues. + /// + private void ResetQueues() + { + _localConnectionStates.Clear(); + base.ClearPacketQueue(ref _incoming); + base.ClearPacketQueue(ref _outgoing); + _remoteConnectionEvents.Clear(); + } + + + /// + /// Called when a peer disconnects or times out. + /// + private void Listener_PeerDisconnectedEvent(NetPeer peer, DisconnectInfo disconnectInfo) + { + _remoteConnectionEvents.Enqueue(new RemoteConnectionEvent(false, peer.Id)); + } + + /// + /// Called when a peer completes connection. + /// + private void Listener_PeerConnectedEvent(NetPeer peer) + { + _remoteConnectionEvents.Enqueue(new RemoteConnectionEvent(true, peer.Id)); + } + + /// + /// Called when data is received from a peer. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Listener_NetworkReceiveEvent(NetPeer fromPeer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod) + { + //If over the MTU. + if (reader.AvailableBytes > _mtu) + { + _remoteConnectionEvents.Enqueue(new RemoteConnectionEvent(false, fromPeer.Id)); + fromPeer.Disconnect(); + } + else + { + base.Listener_NetworkReceiveEvent(_incoming, fromPeer, reader, deliveryMethod, _mtu); + } + } + + + /// + /// Called when a remote connection request is made. + /// + private void Listener_ConnectionRequestEvent(ConnectionRequest request) + { + if (_server == null) + return; + + //At maximum peers. + if (_server.ConnectedPeersCount >= _maximumClients) + { + request.Reject(); + return; + } + + request.AcceptIfKey(_key); + } + + /// + /// Dequeues and processes outgoing. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DequeueOutgoing() + { + if (base.GetConnectionState() != LocalConnectionState.Started || _server == null) + { + //Not started, clear outgoing. + base.ClearPacketQueue(ref _outgoing); + } + else + { + int count = _outgoing.Count; + for (int i = 0; i < count; i++) + { + Packet outgoing = _outgoing.Dequeue(); + int connectionId = outgoing.ConnectionId; + + ArraySegment segment = outgoing.GetArraySegment(); + DeliveryMethod dm = (outgoing.Channel == (byte)Channel.Reliable) ? + DeliveryMethod.ReliableOrdered : DeliveryMethod.Unreliable; + + //If over the MTU. + if (outgoing.Channel == (byte)Channel.Unreliable && segment.Count > _mtu) + { + base.Transport.NetworkManager.LogWarning($"Server is sending of {segment.Count} length on the unreliable channel, while the MTU is only {_mtu}. The channel has been changed to reliable for this send."); + dm = DeliveryMethod.ReliableOrdered; + } + + //Send to all clients. + if (connectionId == NetworkConnection.UNSET_CLIENTID_VALUE) + { + _server.SendToAll(segment.Array, segment.Offset, segment.Count, dm); + } + //Send to one client. + else + { + NetPeer peer = GetNetPeer(connectionId, true); + //If peer is found. + if (peer != null) + peer.Send(segment.Array, segment.Offset, segment.Count, dm); + } + + outgoing.Dispose(); + } + } + } + + /// + /// Allows for Outgoing queue to be iterated. + /// + internal void IterateOutgoing() + { + DequeueOutgoing(); + } + + /// + /// Iterates the Incoming queue. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void IterateIncoming() + { + _server?.PollEvents(); + + /* Run local connection states first so we can begin + * to read for data at the start of the frame, as that's + * where incoming is read. */ + while (_localConnectionStates.Count > 0) + base.SetConnectionState(_localConnectionStates.Dequeue(), true); + + //Not yet started. + LocalConnectionState localState = base.GetConnectionState(); + if (localState != LocalConnectionState.Started) + { + ResetQueues(); + //If stopped try to kill task. + if (localState == LocalConnectionState.Stopped) + { + StopSocketOnThread(); + return; + } + } + + //Handle connection and disconnection events. + while (_remoteConnectionEvents.Count > 0) + { + RemoteConnectionEvent connectionEvent = _remoteConnectionEvents.Dequeue(); + RemoteConnectionState state = (connectionEvent.Connected) ? RemoteConnectionState.Started : RemoteConnectionState.Stopped; + base.Transport.HandleRemoteConnectionState(new RemoteConnectionStateArgs(state, connectionEvent.ConnectionId, base.Transport.Index)); + } + + //Handle packets. + while (_incoming.Count > 0) + { + Packet incoming = _incoming.Dequeue(); + //Make sure peer is still connected. + NetPeer peer = GetNetPeer(incoming.ConnectionId, true); + if (peer != null) + { + ServerReceivedDataArgs dataArgs = new ServerReceivedDataArgs( + incoming.GetArraySegment(), + (Channel)incoming.Channel, + incoming.ConnectionId, + base.Transport.Index); + + base.Transport.HandleServerReceivedDataArgs(dataArgs); + } + + incoming.Dispose(); + } + + } + + /// + /// Sends a packet to a single, or all clients. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void SendToClient(byte channelId, ArraySegment segment, int connectionId) + { + Send(ref _outgoing, channelId, segment, connectionId, _mtu); + } + + /// + /// Returns the maximum number of clients allowed to connect to the server. If the transport does not support this method the value -1 is returned. + /// + /// + internal int GetMaximumClients() + { + return _maximumClients; + } + + /// + /// Sets the MaximumClients value. + /// + /// + internal void SetMaximumClients(int value) + { + _maximumClients = value; + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ServerSocket.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ServerSocket.cs.meta new file mode 100644 index 0000000..9cb8afc --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ServerSocket.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5c6703e8024041e45ae92566123865ad +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/Supporting.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/Supporting.cs new file mode 100644 index 0000000..af55ed3 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/Supporting.cs @@ -0,0 +1,63 @@ +using FishNet.Utility.Performance; +using System; + +namespace FishNet.Transporting.Tugboat +{ + + + internal struct Packet + { + public readonly int ConnectionId; + public readonly byte[] Data; + public readonly int Length; + public readonly byte Channel; + + public Packet(int connectionId, byte[] data, int length, byte channel) + { + ConnectionId = connectionId; + Data = data; + Length = length; + Channel = channel; + } + + public Packet(int sender, ArraySegment segment, byte channel, int mtu) + { + //Prefer to max out returned array to mtu to reduce chance of resizing. + int arraySize = Math.Max(segment.Count, mtu); + Data = ByteArrayPool.Retrieve(arraySize); + Buffer.BlockCopy(segment.Array, segment.Offset, Data, 0, segment.Count); + ConnectionId = sender; + Length = segment.Count; + Channel = channel; + } + + public ArraySegment GetArraySegment() + { + return new ArraySegment(Data, 0, Length); + } + + public void Dispose() + { + ByteArrayPool.Store(Data); + } + + } + + +} + +namespace FishNet.Transporting.Tugboat.Server +{ + + internal struct RemoteConnectionEvent + { + public readonly bool Connected; + public readonly int ConnectionId; + public RemoteConnectionEvent(bool connected, int connectionId) + { + Connected = connected; + ConnectionId = connectionId; + } + } +} + diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/Supporting.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/Supporting.cs.meta new file mode 100644 index 0000000..307a74f --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/Supporting.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 80c810a1a6a8f3345bb48abfb75c804a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib.meta new file mode 100644 index 0000000..6708b0d --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9af335f3230cff649a5cc0c50e34a206 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/BaseChannel.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/BaseChannel.cs new file mode 100644 index 0000000..261839d --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/BaseChannel.cs @@ -0,0 +1,46 @@ +using System.Collections.Concurrent; +using System.Threading; + +namespace LiteNetLib +{ + internal abstract class BaseChannel + { + protected readonly NetPeer Peer; + protected readonly ConcurrentQueue OutgoingQueue; + private int _isAddedToPeerChannelSendQueue; + + public int PacketsInQueue => OutgoingQueue.Count; + + protected BaseChannel(NetPeer peer) + { + Peer = peer; + OutgoingQueue = new ConcurrentQueue(); + } + + public void AddToQueue(NetPacket packet) + { + OutgoingQueue.Enqueue(packet); + AddToPeerChannelSendQueue(); + } + + protected void AddToPeerChannelSendQueue() + { + if (Interlocked.CompareExchange(ref _isAddedToPeerChannelSendQueue, 1, 0) == 0) + { + Peer.AddToReliableChannelSendQueue(this); + } + } + + public bool SendAndCheckQueue() + { + bool hasPacketsToSend = SendNextPackets(); + if (!hasPacketsToSend) + Interlocked.Exchange(ref _isAddedToPeerChannelSendQueue, 0); + + return hasPacketsToSend; + } + + protected abstract bool SendNextPackets(); + public abstract bool ProcessPacket(NetPacket packet); + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/BaseChannel.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/BaseChannel.cs.meta new file mode 100644 index 0000000..0d6a9bf --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/BaseChannel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 70b7c357a9f57f5479c5d94550d26280 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ConnectionRequest.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ConnectionRequest.cs new file mode 100644 index 0000000..4a2cdd9 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ConnectionRequest.cs @@ -0,0 +1,134 @@ +using System.Net; +using System.Threading; +using LiteNetLib.Utils; + +namespace LiteNetLib +{ + internal enum ConnectionRequestResult + { + None, + Accept, + Reject, + RejectForce + } + + public class ConnectionRequest + { + private readonly NetManager _listener; + private int _used; + + public NetDataReader Data => InternalPacket.Data; + + internal ConnectionRequestResult Result { get; private set; } + internal NetConnectRequestPacket InternalPacket; + + public readonly IPEndPoint RemoteEndPoint; + + internal void UpdateRequest(NetConnectRequestPacket connectRequest) + { + //old request + if (connectRequest.ConnectionTime < InternalPacket.ConnectionTime) + return; + + if (connectRequest.ConnectionTime == InternalPacket.ConnectionTime && + connectRequest.ConnectionNumber == InternalPacket.ConnectionNumber) + return; + + InternalPacket = connectRequest; + } + + private bool TryActivate() + { + return Interlocked.CompareExchange(ref _used, 1, 0) == 0; + } + + internal ConnectionRequest(IPEndPoint remoteEndPoint, NetConnectRequestPacket requestPacket, NetManager listener) + { + InternalPacket = requestPacket; + RemoteEndPoint = remoteEndPoint; + _listener = listener; + } + + public NetPeer AcceptIfKey(string key) + { + if (!TryActivate()) + return null; + try + { + if (Data.GetString() == key) + Result = ConnectionRequestResult.Accept; + } + catch + { + NetDebug.WriteError("[AC] Invalid incoming data"); + } + if (Result == ConnectionRequestResult.Accept) + return _listener.OnConnectionSolved(this, null, 0, 0); + + Result = ConnectionRequestResult.Reject; + _listener.OnConnectionSolved(this, null, 0, 0); + return null; + } + + /// + /// Accept connection and get new NetPeer as result + /// + /// Connected NetPeer + public NetPeer Accept() + { + if (!TryActivate()) + return null; + Result = ConnectionRequestResult.Accept; + return _listener.OnConnectionSolved(this, null, 0, 0); + } + + public void Reject(byte[] rejectData, int start, int length, bool force) + { + if (!TryActivate()) + return; + Result = force ? ConnectionRequestResult.RejectForce : ConnectionRequestResult.Reject; + _listener.OnConnectionSolved(this, rejectData, start, length); + } + + public void Reject(byte[] rejectData, int start, int length) + { + Reject(rejectData, start, length, false); + } + + + public void RejectForce(byte[] rejectData, int start, int length) + { + Reject(rejectData, start, length, true); + } + + public void RejectForce() + { + Reject(null, 0, 0, true); + } + + public void RejectForce(byte[] rejectData) + { + Reject(rejectData, 0, rejectData.Length, true); + } + + public void RejectForce(NetDataWriter rejectData) + { + Reject(rejectData.Data, 0, rejectData.Length, true); + } + + public void Reject() + { + Reject(null, 0, 0, false); + } + + public void Reject(byte[] rejectData) + { + Reject(rejectData, 0, rejectData.Length, false); + } + + public void Reject(NetDataWriter rejectData) + { + Reject(rejectData.Data, 0, rejectData.Length, false); + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ConnectionRequest.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ConnectionRequest.cs.meta new file mode 100644 index 0000000..f1c5f19 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ConnectionRequest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5e609855f95e9034889c882b51aaec68 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/INetEventListener.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/INetEventListener.cs new file mode 100644 index 0000000..13d8852 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/INetEventListener.cs @@ -0,0 +1,272 @@ +using System.Net; +using System.Net.Sockets; +using LiteNetLib.Utils; + +namespace LiteNetLib +{ + /// + /// Type of message that you receive in OnNetworkReceiveUnconnected event + /// + public enum UnconnectedMessageType + { + BasicMessage, + Broadcast + } + + /// + /// Disconnect reason that you receive in OnPeerDisconnected event + /// + public enum DisconnectReason + { + ConnectionFailed, + Timeout, + HostUnreachable, + NetworkUnreachable, + RemoteConnectionClose, + DisconnectPeerCalled, + ConnectionRejected, + InvalidProtocol, + UnknownHost, + Reconnect, + PeerToPeerConnection, + PeerNotFound + } + + /// + /// Additional information about disconnection + /// + public struct DisconnectInfo + { + /// + /// Additional info why peer disconnected + /// + public DisconnectReason Reason; + + /// + /// Error code (if reason is SocketSendError or SocketReceiveError) + /// + public SocketError SocketErrorCode; + + /// + /// Additional data that can be accessed (only if reason is RemoteConnectionClose) + /// + public NetPacketReader AdditionalData; + } + + public interface INetEventListener + { + /// + /// New remote peer connected to host, or client connected to remote host + /// + /// Connected peer object + void OnPeerConnected(NetPeer peer); + + /// + /// Peer disconnected + /// + /// disconnected peer + /// additional info about reason, errorCode or data received with disconnect message + void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo); + + /// + /// Network error (on send or receive) + /// + /// From endPoint (can be null) + /// Socket error + void OnNetworkError(IPEndPoint endPoint, SocketError socketError); + + /// + /// Received some data + /// + /// From peer + /// DataReader containing all received data + /// Number of channel at which packet arrived + /// Type of received packet + void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod); + + /// + /// Received unconnected message + /// + /// From address (IP and Port) + /// Message data + /// Message type (simple, discovery request or response) + void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType); + + /// + /// Latency information updated + /// + /// Peer with updated latency + /// latency value in milliseconds + void OnNetworkLatencyUpdate(NetPeer peer, int latency); + + /// + /// On peer connection requested + /// + /// Request information (EndPoint, internal id, additional data) + void OnConnectionRequest(ConnectionRequest request); + } + + public interface IDeliveryEventListener + { + /// + /// On reliable message delivered + /// + /// + /// + void OnMessageDelivered(NetPeer peer, object userData); + } + + public interface INtpEventListener + { + /// + /// Ntp response + /// + /// + void OnNtpResponse(NtpPacket packet); + } + + public interface IPeerAddressChangedListener + { + /// + /// Called when peer address changed (when AllowPeerAddressChange is enabled) + /// + /// Peer that changed address (with new address) + /// previous IP + void OnPeerAddressChanged(NetPeer peer, IPEndPoint previousAddress); + } + + public class EventBasedNetListener : INetEventListener, IDeliveryEventListener, INtpEventListener, IPeerAddressChangedListener + { + public delegate void OnPeerConnected(NetPeer peer); + public delegate void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo); + public delegate void OnNetworkError(IPEndPoint endPoint, SocketError socketError); + public delegate void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod); + public delegate void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType); + public delegate void OnNetworkLatencyUpdate(NetPeer peer, int latency); + public delegate void OnConnectionRequest(ConnectionRequest request); + public delegate void OnDeliveryEvent(NetPeer peer, object userData); + public delegate void OnNtpResponseEvent(NtpPacket packet); + public delegate void OnPeerAddressChangedEvent(NetPeer peer, IPEndPoint previousAddress); + + public event OnPeerConnected PeerConnectedEvent; + public event OnPeerDisconnected PeerDisconnectedEvent; + public event OnNetworkError NetworkErrorEvent; + public event OnNetworkReceive NetworkReceiveEvent; + public event OnNetworkReceiveUnconnected NetworkReceiveUnconnectedEvent; + public event OnNetworkLatencyUpdate NetworkLatencyUpdateEvent; + public event OnConnectionRequest ConnectionRequestEvent; + public event OnDeliveryEvent DeliveryEvent; + public event OnNtpResponseEvent NtpResponseEvent; + public event OnPeerAddressChangedEvent PeerAddressChangedEvent; + + public void ClearPeerConnectedEvent() + { + PeerConnectedEvent = null; + } + + public void ClearPeerDisconnectedEvent() + { + PeerDisconnectedEvent = null; + } + + public void ClearNetworkErrorEvent() + { + NetworkErrorEvent = null; + } + + public void ClearNetworkReceiveEvent() + { + NetworkReceiveEvent = null; + } + + public void ClearNetworkReceiveUnconnectedEvent() + { + NetworkReceiveUnconnectedEvent = null; + } + + public void ClearNetworkLatencyUpdateEvent() + { + NetworkLatencyUpdateEvent = null; + } + + public void ClearConnectionRequestEvent() + { + ConnectionRequestEvent = null; + } + + public void ClearDeliveryEvent() + { + DeliveryEvent = null; + } + + public void ClearNtpResponseEvent() + { + NtpResponseEvent = null; + } + + public void ClearPeerAddressChangedEvent() + { + PeerAddressChangedEvent = null; + } + + void INetEventListener.OnPeerConnected(NetPeer peer) + { + if (PeerConnectedEvent != null) + PeerConnectedEvent(peer); + } + + void INetEventListener.OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) + { + if (PeerDisconnectedEvent != null) + PeerDisconnectedEvent(peer, disconnectInfo); + } + + void INetEventListener.OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode) + { + if (NetworkErrorEvent != null) + NetworkErrorEvent(endPoint, socketErrorCode); + } + + void INetEventListener.OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod) + { + if (NetworkReceiveEvent != null) + NetworkReceiveEvent(peer, reader, channelNumber, deliveryMethod); + } + + void INetEventListener.OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) + { + if (NetworkReceiveUnconnectedEvent != null) + NetworkReceiveUnconnectedEvent(remoteEndPoint, reader, messageType); + } + + void INetEventListener.OnNetworkLatencyUpdate(NetPeer peer, int latency) + { + if (NetworkLatencyUpdateEvent != null) + NetworkLatencyUpdateEvent(peer, latency); + } + + void INetEventListener.OnConnectionRequest(ConnectionRequest request) + { + if (ConnectionRequestEvent != null) + ConnectionRequestEvent(request); + } + + void IDeliveryEventListener.OnMessageDelivered(NetPeer peer, object userData) + { + if (DeliveryEvent != null) + DeliveryEvent(peer, userData); + } + + void INtpEventListener.OnNtpResponse(NtpPacket packet) + { + if (NtpResponseEvent != null) + NtpResponseEvent(packet); + } + + void IPeerAddressChangedListener.OnPeerAddressChanged(NetPeer peer, IPEndPoint previousAddress) + { + if (PeerAddressChangedEvent != null) + PeerAddressChangedEvent(peer, previousAddress); + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/INetEventListener.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/INetEventListener.cs.meta new file mode 100644 index 0000000..d4fc4bb --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/INetEventListener.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 79436ed5864cf48418ac341ea3c70a6b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/InternalPackets.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/InternalPackets.cs new file mode 100644 index 0000000..2eb09fe --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/InternalPackets.cs @@ -0,0 +1,133 @@ +using System; +using System.Net; +using LiteNetLib.Utils; + +namespace LiteNetLib +{ + internal sealed class NetConnectRequestPacket + { + public const int HeaderSize = 18; + public readonly long ConnectionTime; + public byte ConnectionNumber; + public readonly byte[] TargetAddress; + public readonly NetDataReader Data; + public readonly int PeerId; + + private NetConnectRequestPacket(long connectionTime, byte connectionNumber, int localId, byte[] targetAddress, NetDataReader data) + { + ConnectionTime = connectionTime; + ConnectionNumber = connectionNumber; + TargetAddress = targetAddress; + Data = data; + PeerId = localId; + } + + public static int GetProtocolId(NetPacket packet) + { + return BitConverter.ToInt32(packet.RawData, 1); + } + + public static NetConnectRequestPacket FromData(NetPacket packet) + { + if (packet.ConnectionNumber >= NetConstants.MaxConnectionNumber) + return null; + + //Getting connection time for peer + long connectionTime = BitConverter.ToInt64(packet.RawData, 5); + + //Get peer id + int peerId = BitConverter.ToInt32(packet.RawData, 13); + + //Get target address + int addrSize = packet.RawData[HeaderSize-1]; + if (addrSize != 16 && addrSize != 28) + return null; + byte[] addressBytes = new byte[addrSize]; + Buffer.BlockCopy(packet.RawData, HeaderSize, addressBytes, 0, addrSize); + + // Read data and create request + var reader = new NetDataReader(null, 0, 0); + if (packet.Size > HeaderSize+addrSize) + reader.SetSource(packet.RawData, HeaderSize + addrSize, packet.Size); + + return new NetConnectRequestPacket(connectionTime, packet.ConnectionNumber, peerId, addressBytes, reader); + } + + public static NetPacket Make(NetDataWriter connectData, SocketAddress addressBytes, long connectTime, int localId) + { + //Make initial packet + var packet = new NetPacket(PacketProperty.ConnectRequest, connectData.Length+addressBytes.Size); + + //Add data + FastBitConverter.GetBytes(packet.RawData, 1, NetConstants.ProtocolId); + FastBitConverter.GetBytes(packet.RawData, 5, connectTime); + FastBitConverter.GetBytes(packet.RawData, 13, localId); + packet.RawData[HeaderSize-1] = (byte)addressBytes.Size; + for (int i = 0; i < addressBytes.Size; i++) + packet.RawData[HeaderSize + i] = addressBytes[i]; + Buffer.BlockCopy(connectData.Data, 0, packet.RawData, HeaderSize + addressBytes.Size, connectData.Length); + return packet; + } + } + + internal sealed class NetConnectAcceptPacket + { + public const int Size = 15; + public readonly long ConnectionTime; + public readonly byte ConnectionNumber; + public readonly int PeerId; + public readonly bool PeerNetworkChanged; + + private NetConnectAcceptPacket(long connectionTime, byte connectionNumber, int peerId, bool peerNetworkChanged) + { + ConnectionTime = connectionTime; + ConnectionNumber = connectionNumber; + PeerId = peerId; + PeerNetworkChanged = peerNetworkChanged; + } + + public static NetConnectAcceptPacket FromData(NetPacket packet) + { + if (packet.Size != Size) + return null; + + long connectionId = BitConverter.ToInt64(packet.RawData, 1); + + //check connect num + byte connectionNumber = packet.RawData[9]; + if (connectionNumber >= NetConstants.MaxConnectionNumber) + return null; + + //check reused flag + byte isReused = packet.RawData[10]; + if (isReused > 1) + return null; + + //get remote peer id + int peerId = BitConverter.ToInt32(packet.RawData, 11); + if (peerId < 0) + return null; + + return new NetConnectAcceptPacket(connectionId, connectionNumber, peerId, isReused == 1); + } + + public static NetPacket Make(long connectTime, byte connectNum, int localPeerId) + { + var packet = new NetPacket(PacketProperty.ConnectAccept, 0); + FastBitConverter.GetBytes(packet.RawData, 1, connectTime); + packet.RawData[9] = connectNum; + FastBitConverter.GetBytes(packet.RawData, 11, localPeerId); + return packet; + } + + public static NetPacket MakeNetworkChanged(NetPeer peer) + { + var packet = new NetPacket(PacketProperty.PeerNotFound, Size-1); + FastBitConverter.GetBytes(packet.RawData, 1, peer.ConnectTime); + packet.RawData[9] = peer.ConnectionNum; + packet.RawData[10] = 1; + FastBitConverter.GetBytes(packet.RawData, 11, peer.RemoteId); + return packet; + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/InternalPackets.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/InternalPackets.cs.meta new file mode 100644 index 0000000..5415d5d --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/InternalPackets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b8582459906515843a2f2adb010c3fd7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers.meta new file mode 100644 index 0000000..f2e0b72 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bcd79f96aed490043aa493beed54d929 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/Crc32cLayer.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/Crc32cLayer.cs new file mode 100644 index 0000000..3ee97d6 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/Crc32cLayer.cs @@ -0,0 +1,41 @@ +using LiteNetLib.Utils; +using System; +using System.Net; + +namespace LiteNetLib.Layers +{ + public sealed class Crc32cLayer : PacketLayerBase + { + public Crc32cLayer() : base(CRC32C.ChecksumSize) + { + + } + + public override void ProcessInboundPacket(ref IPEndPoint endPoint, ref byte[] data, ref int offset, ref int length) + { + if (length < NetConstants.HeaderSize + CRC32C.ChecksumSize) + { + NetDebug.WriteError("[NM] DataReceived size: bad!"); + //Set length to 0 to have netManager drop the packet. + length = 0; + return; + } + + int checksumPoint = length - CRC32C.ChecksumSize; + if (CRC32C.Compute(data, offset, checksumPoint) != BitConverter.ToUInt32(data, checksumPoint)) + { + NetDebug.Write("[NM] DataReceived checksum: bad!"); + //Set length to 0 to have netManager drop the packet. + length = 0; + return; + } + length -= CRC32C.ChecksumSize; + } + + public override void ProcessOutBoundPacket(ref IPEndPoint endPoint, ref byte[] data, ref int offset, ref int length) + { + FastBitConverter.GetBytes(data, length, CRC32C.Compute(data, offset, length)); + length += CRC32C.ChecksumSize; + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/Crc32cLayer.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/Crc32cLayer.cs.meta new file mode 100644 index 0000000..45504b8 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/Crc32cLayer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7d52aafc1486ec842a8d133ef41dfd39 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/PacketLayerBase.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/PacketLayerBase.cs new file mode 100644 index 0000000..b3d9b3a --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/PacketLayerBase.cs @@ -0,0 +1,17 @@ +using System.Net; + +namespace LiteNetLib.Layers +{ + public abstract class PacketLayerBase + { + public readonly int ExtraPacketSizeForLayer; + + protected PacketLayerBase(int extraPacketSizeForLayer) + { + ExtraPacketSizeForLayer = extraPacketSizeForLayer; + } + + public abstract void ProcessInboundPacket(ref IPEndPoint endPoint, ref byte[] data, ref int offset, ref int length); + public abstract void ProcessOutBoundPacket(ref IPEndPoint endPoint, ref byte[] data, ref int offset, ref int length); + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/PacketLayerBase.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/PacketLayerBase.cs.meta new file mode 100644 index 0000000..43aaedb --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/PacketLayerBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bddd0b5590cce7e42918c72429b84bde +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/XorEncryptLayer.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/XorEncryptLayer.cs new file mode 100644 index 0000000..9b67196 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/XorEncryptLayer.cs @@ -0,0 +1,60 @@ +using System; +using System.Net; +using System.Text; + +namespace LiteNetLib.Layers +{ + public class XorEncryptLayer : PacketLayerBase + { + private byte[] _byteKey; + + public XorEncryptLayer() : base(0) + { + + } + + public XorEncryptLayer(byte[] key) : this() + { + SetKey(key); + } + + public XorEncryptLayer(string key) : this() + { + SetKey(key); + } + + public void SetKey(string key) + { + _byteKey = Encoding.UTF8.GetBytes(key); + } + + public void SetKey(byte[] key) + { + if (_byteKey == null || _byteKey.Length != key.Length) + _byteKey = new byte[key.Length]; + Buffer.BlockCopy(key, 0, _byteKey, 0, key.Length); + } + + public override void ProcessInboundPacket(ref IPEndPoint endPoint, ref byte[] data, ref int offset, ref int length) + { + if (_byteKey == null) + return; + var cur = offset; + for (var i = 0; i < length; i++, cur++) + { + data[cur] = (byte)(data[cur] ^ _byteKey[i % _byteKey.Length]); + } + } + + public override void ProcessOutBoundPacket(ref IPEndPoint endPoint, ref byte[] data, ref int offset, ref int length) + { + if (_byteKey == null) + return; + var cur = offset; + for (var i = 0; i < length; i++, cur++) + { + data[cur] = (byte)(data[cur] ^ _byteKey[i % _byteKey.Length]); + } + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/XorEncryptLayer.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/XorEncryptLayer.cs.meta new file mode 100644 index 0000000..14a4601 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/XorEncryptLayer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fb37499ecdd1ce145b0bcaf4b4f0279e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/LiteNetLib.csproj.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/LiteNetLib.csproj.meta new file mode 100644 index 0000000..f9c48e9 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/LiteNetLib.csproj.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2ad85b0f43f25f1499c27a4dca23ddd8 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NatPunchModule.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NatPunchModule.cs new file mode 100644 index 0000000..bab2fca --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NatPunchModule.cs @@ -0,0 +1,259 @@ +using System.Collections.Concurrent; +using System.Net; +using LiteNetLib.Utils; + +namespace LiteNetLib +{ + public enum NatAddressType + { + Internal, + External + } + + public interface INatPunchListener + { + void OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token); + void OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token); + } + + public class EventBasedNatPunchListener : INatPunchListener + { + public delegate void OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token); + public delegate void OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token); + + public event OnNatIntroductionRequest NatIntroductionRequest; + public event OnNatIntroductionSuccess NatIntroductionSuccess; + + void INatPunchListener.OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token) + { + if(NatIntroductionRequest != null) + NatIntroductionRequest(localEndPoint, remoteEndPoint, token); + } + + void INatPunchListener.OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token) + { + if (NatIntroductionSuccess != null) + NatIntroductionSuccess(targetEndPoint, type, token); + } + } + + /// + /// Module for UDP NAT Hole punching operations. Can be accessed from NetManager + /// + public sealed class NatPunchModule + { + struct RequestEventData + { + public IPEndPoint LocalEndPoint; + public IPEndPoint RemoteEndPoint; + public string Token; + } + + struct SuccessEventData + { + public IPEndPoint TargetEndPoint; + public NatAddressType Type; + public string Token; + } + + class NatIntroduceRequestPacket + { + public IPEndPoint Internal { get; set; } + public string Token { get; set; } + } + + class NatIntroduceResponsePacket + { + public IPEndPoint Internal { get; set; } + public IPEndPoint External { get; set; } + public string Token { get; set; } + } + + class NatPunchPacket + { + public string Token { get; set; } + public bool IsExternal { get; set; } + } + + private readonly NetManager _socket; + private readonly ConcurrentQueue _requestEvents = new ConcurrentQueue(); + private readonly ConcurrentQueue _successEvents = new ConcurrentQueue(); + private readonly NetDataReader _cacheReader = new NetDataReader(); + private readonly NetDataWriter _cacheWriter = new NetDataWriter(); + private readonly NetPacketProcessor _netPacketProcessor = new NetPacketProcessor(MaxTokenLength); + private INatPunchListener _natPunchListener; + public const int MaxTokenLength = 256; + + /// + /// Events automatically will be called without PollEvents method from another thread + /// + public bool UnsyncedEvents = false; + + internal NatPunchModule(NetManager socket) + { + _socket = socket; + _netPacketProcessor.SubscribeReusable(OnNatIntroductionResponse); + _netPacketProcessor.SubscribeReusable(OnNatIntroductionRequest); + _netPacketProcessor.SubscribeReusable(OnNatPunch); + } + + internal void ProcessMessage(IPEndPoint senderEndPoint, NetPacket packet) + { + lock (_cacheReader) + { + _cacheReader.SetSource(packet.RawData, NetConstants.HeaderSize, packet.Size); + _netPacketProcessor.ReadAllPackets(_cacheReader, senderEndPoint); + } + } + + public void Init(INatPunchListener listener) + { + _natPunchListener = listener; + } + + private void Send(T packet, IPEndPoint target) where T : class, new() + { + _cacheWriter.Reset(); + _cacheWriter.Put((byte)PacketProperty.NatMessage); + _netPacketProcessor.Write(_cacheWriter, packet); + _socket.SendRaw(_cacheWriter.Data, 0, _cacheWriter.Length, target); + } + + public void NatIntroduce( + IPEndPoint hostInternal, + IPEndPoint hostExternal, + IPEndPoint clientInternal, + IPEndPoint clientExternal, + string additionalInfo) + { + var req = new NatIntroduceResponsePacket + { + Token = additionalInfo + }; + + //First packet (server) send to client + req.Internal = hostInternal; + req.External = hostExternal; + Send(req, clientExternal); + + //Second packet (client) send to server + req.Internal = clientInternal; + req.External = clientExternal; + Send(req, hostExternal); + } + + public void PollEvents() + { + if (UnsyncedEvents) + return; + + if (_natPunchListener == null || (_successEvents.IsEmpty && _requestEvents.IsEmpty)) + return; + + while (_successEvents.TryDequeue(out var evt)) + { + _natPunchListener.OnNatIntroductionSuccess( + evt.TargetEndPoint, + evt.Type, + evt.Token); + } + + while (_requestEvents.TryDequeue(out var evt)) + { + _natPunchListener.OnNatIntroductionRequest(evt.LocalEndPoint, evt.RemoteEndPoint, evt.Token); + } + } + + public void SendNatIntroduceRequest(string host, int port, string additionalInfo) + { + SendNatIntroduceRequest(NetUtils.MakeEndPoint(host, port), additionalInfo); + } + + public void SendNatIntroduceRequest(IPEndPoint masterServerEndPoint, string additionalInfo) + { + //prepare outgoing data + string networkIp = NetUtils.GetLocalIp(LocalAddrType.IPv4); + if (string.IsNullOrEmpty(networkIp)) + { + networkIp = NetUtils.GetLocalIp(LocalAddrType.IPv6); + } + + Send( + new NatIntroduceRequestPacket + { + Internal = NetUtils.MakeEndPoint(networkIp, _socket.LocalPort), + Token = additionalInfo + }, + masterServerEndPoint); + } + + //We got request and must introduce + private void OnNatIntroductionRequest(NatIntroduceRequestPacket req, IPEndPoint senderEndPoint) + { + if (UnsyncedEvents) + { + _natPunchListener.OnNatIntroductionRequest( + req.Internal, + senderEndPoint, + req.Token); + } + else + { + _requestEvents.Enqueue(new RequestEventData + { + LocalEndPoint = req.Internal, + RemoteEndPoint = senderEndPoint, + Token = req.Token + }); + } + } + + //We got introduce and must punch + private void OnNatIntroductionResponse(NatIntroduceResponsePacket req) + { + NetDebug.Write(NetLogLevel.Trace, "[NAT] introduction received"); + + // send internal punch + var punchPacket = new NatPunchPacket {Token = req.Token}; + Send(punchPacket, req.Internal); + NetDebug.Write(NetLogLevel.Trace, "[NAT] internal punch sent to " + req.Internal); + + // hack for some routers + _socket.Ttl = 2; + _socket.SendRaw(new[] { (byte)PacketProperty.Empty }, 0, 1, req.External); + + // send external punch + _socket.Ttl = NetConstants.SocketTTL; + punchPacket.IsExternal = true; + Send(punchPacket, req.External); + NetDebug.Write(NetLogLevel.Trace, "[NAT] external punch sent to " + req.External); + } + + //We got punch and can connect + private void OnNatPunch(NatPunchPacket req, IPEndPoint senderEndPoint) + { + //Read info + NetDebug.Write(NetLogLevel.Trace, "[NAT] punch received from {0} - additional info: {1}", + senderEndPoint, req.Token); + + //Release punch success to client; enabling him to Connect() to Sender if token is ok + if(UnsyncedEvents) + { + _natPunchListener.OnNatIntroductionSuccess( + senderEndPoint, + req.IsExternal ? NatAddressType.External : NatAddressType.Internal, + req.Token + ); + } + else + { + _successEvents.Enqueue(new SuccessEventData + { + TargetEndPoint = senderEndPoint, + Type = req.IsExternal ? NatAddressType.External : NatAddressType.Internal, + Token = req.Token + }); + } + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NatPunchModule.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NatPunchModule.cs.meta new file mode 100644 index 0000000..887acb2 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NatPunchModule.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e608aa88ab820244a90b83eca0f716c5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NativeSocket.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NativeSocket.cs new file mode 100644 index 0000000..fc846c8 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NativeSocket.cs @@ -0,0 +1,301 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LiteNetLib +{ + internal readonly struct NativeAddr : IEquatable + { + //common parts + private readonly long _part1; //family, port, etc + private readonly long _part2; + //ipv6 parts + private readonly long _part3; + private readonly int _part4; + + private readonly int _hash; + + public NativeAddr(byte[] address, int len) + { + _part1 = BitConverter.ToInt64(address, 0); + _part2 = BitConverter.ToInt64(address, 8); + if (len > 16) + { + _part3 = BitConverter.ToInt64(address, 16); + _part4 = BitConverter.ToInt32(address, 24); + } + else + { + _part3 = 0; + _part4 = 0; + } + _hash = (int)(_part1 >> 32) ^ (int)_part1 ^ + (int)(_part2 >> 32) ^ (int)_part2 ^ + (int)(_part3 >> 32) ^ (int)_part3 ^ + _part4; + } + + public override int GetHashCode() + { + return _hash; + } + + public bool Equals(NativeAddr other) + { + return _part1 == other._part1 && + _part2 == other._part2 && + _part3 == other._part3 && + _part4 == other._part4; + } + + public override bool Equals(object obj) + { + return obj is NativeAddr other && Equals(other); + } + + public static bool operator ==(NativeAddr left, NativeAddr right) + { + return left.Equals(right); + } + + public static bool operator !=(NativeAddr left, NativeAddr right) + { + return !left.Equals(right); + } + } + + internal class NativeEndPoint : IPEndPoint + { + public readonly byte[] NativeAddress; + + public NativeEndPoint(byte[] address) : base(IPAddress.Any, 0) + { + NativeAddress = new byte[address.Length]; + Buffer.BlockCopy(address, 0, NativeAddress, 0, address.Length); + + short family = (short)((address[1] << 8) | address[0]); + Port =(ushort)((address[2] << 8) | address[3]); + + if ((NativeSocket.UnixMode && family == NativeSocket.AF_INET6) || (!NativeSocket.UnixMode && (AddressFamily)family == AddressFamily.InterNetworkV6)) + { + uint scope = unchecked((uint)( + (address[27] << 24) + + (address[26] << 16) + + (address[25] << 8) + + (address[24]))); +#if NETCOREAPP || NETSTANDARD2_1 || NETSTANDARD2_1_OR_GREATER + Address = new IPAddress(new ReadOnlySpan(address, 8, 16), scope); +#else + byte[] addrBuffer = new byte[16]; + Buffer.BlockCopy(address, 8, addrBuffer, 0, 16); + Address = new IPAddress(addrBuffer, scope); +#endif + } + else //IPv4 + { + long ipv4Addr = unchecked((uint)((address[4] & 0x000000FF) | + (address[5] << 8 & 0x0000FF00) | + (address[6] << 16 & 0x00FF0000) | + (address[7] << 24))); + Address = new IPAddress(ipv4Addr); + } + } + } + + internal static class NativeSocket + { + static +#if LITENETLIB_UNSAFE + unsafe +#endif + class WinSock + { + private const string LibName = "ws2_32.dll"; + + [DllImport(LibName, SetLastError = true)] + public static extern int recvfrom( + IntPtr socketHandle, + [In, Out] byte[] pinnedBuffer, + [In] int len, + [In] SocketFlags socketFlags, + [Out] byte[] socketAddress, + [In, Out] ref int socketAddressSize); + + [DllImport(LibName, SetLastError = true)] + internal static extern int sendto( + IntPtr socketHandle, +#if LITENETLIB_UNSAFE + byte* pinnedBuffer, +#else + [In] byte[] pinnedBuffer, +#endif + [In] int len, + [In] SocketFlags socketFlags, + [In] byte[] socketAddress, + [In] int socketAddressSize); + } + + static +#if LITENETLIB_UNSAFE + unsafe +#endif + class UnixSock + { + private const string LibName = "libc"; + + [DllImport(LibName, SetLastError = true)] + public static extern int recvfrom( + IntPtr socketHandle, + [In, Out] byte[] pinnedBuffer, + [In] int len, + [In] SocketFlags socketFlags, + [Out] byte[] socketAddress, + [In, Out] ref int socketAddressSize); + + [DllImport(LibName, SetLastError = true)] + internal static extern int sendto( + IntPtr socketHandle, +#if LITENETLIB_UNSAFE + byte* pinnedBuffer, +#else + [In] byte[] pinnedBuffer, +#endif + [In] int len, + [In] SocketFlags socketFlags, + [In] byte[] socketAddress, + [In] int socketAddressSize); + } + + public static readonly bool IsSupported = false; + public static readonly bool UnixMode = false; + + public const int IPv4AddrSize = 16; + public const int IPv6AddrSize = 28; + public const int AF_INET = 2; + public const int AF_INET6 = 10; + + private static readonly Dictionary NativeErrorToSocketError = new Dictionary + { + { 13, SocketError.AccessDenied }, //EACCES + { 98, SocketError.AddressAlreadyInUse }, //EADDRINUSE + { 99, SocketError.AddressNotAvailable }, //EADDRNOTAVAIL + { 97, SocketError.AddressFamilyNotSupported }, //EAFNOSUPPORT + { 11, SocketError.WouldBlock }, //EAGAIN + { 114, SocketError.AlreadyInProgress }, //EALREADY + { 9, SocketError.OperationAborted }, //EBADF + { 125, SocketError.OperationAborted }, //ECANCELED + { 103, SocketError.ConnectionAborted }, //ECONNABORTED + { 111, SocketError.ConnectionRefused }, //ECONNREFUSED + { 104, SocketError.ConnectionReset }, //ECONNRESET + { 89, SocketError.DestinationAddressRequired }, //EDESTADDRREQ + { 14, SocketError.Fault }, //EFAULT + { 112, SocketError.HostDown }, //EHOSTDOWN + { 6, SocketError.HostNotFound }, //ENXIO + { 113, SocketError.HostUnreachable }, //EHOSTUNREACH + { 115, SocketError.InProgress }, //EINPROGRESS + { 4, SocketError.Interrupted }, //EINTR + { 22, SocketError.InvalidArgument }, //EINVAL + { 106, SocketError.IsConnected }, //EISCONN + { 24, SocketError.TooManyOpenSockets }, //EMFILE + { 90, SocketError.MessageSize }, //EMSGSIZE + { 100, SocketError.NetworkDown }, //ENETDOWN + { 102, SocketError.NetworkReset }, //ENETRESET + { 101, SocketError.NetworkUnreachable }, //ENETUNREACH + { 23, SocketError.TooManyOpenSockets }, //ENFILE + { 105, SocketError.NoBufferSpaceAvailable }, //ENOBUFS + { 61, SocketError.NoData }, //ENODATA + { 2, SocketError.AddressNotAvailable }, //ENOENT + { 92, SocketError.ProtocolOption }, //ENOPROTOOPT + { 107, SocketError.NotConnected }, //ENOTCONN + { 88, SocketError.NotSocket }, //ENOTSOCK + { 3440, SocketError.OperationNotSupported }, //ENOTSUP + { 1, SocketError.AccessDenied }, //EPERM + { 32, SocketError.Shutdown }, //EPIPE + { 96, SocketError.ProtocolFamilyNotSupported }, //EPFNOSUPPORT + { 93, SocketError.ProtocolNotSupported }, //EPROTONOSUPPORT + { 91, SocketError.ProtocolType }, //EPROTOTYPE + { 94, SocketError.SocketNotSupported }, //ESOCKTNOSUPPORT + { 108, SocketError.Disconnecting }, //ESHUTDOWN + { 110, SocketError.TimedOut }, //ETIMEDOUT + { 0, SocketError.Success } + }; + + static NativeSocket() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + IsSupported = true; + UnixMode = true; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + IsSupported = true; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int RecvFrom( + IntPtr socketHandle, + byte[] pinnedBuffer, + int len, + byte[] socketAddress, + ref int socketAddressSize) + { + return UnixMode + ? UnixSock.recvfrom(socketHandle, pinnedBuffer, len, 0, socketAddress, ref socketAddressSize) + : WinSock.recvfrom(socketHandle, pinnedBuffer, len, 0, socketAddress, ref socketAddressSize); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public +#if LITENETLIB_UNSAFE + unsafe +#endif + static int SendTo( + IntPtr socketHandle, +#if LITENETLIB_UNSAFE + byte* pinnedBuffer, +#else + byte[] pinnedBuffer, +#endif + int len, + byte[] socketAddress, + int socketAddressSize) + { + return UnixMode + ? UnixSock.sendto(socketHandle, pinnedBuffer, len, 0, socketAddress, socketAddressSize) + : WinSock.sendto(socketHandle, pinnedBuffer, len, 0, socketAddress, socketAddressSize); + } + + public static SocketError GetSocketError() + { + int error = Marshal.GetLastWin32Error(); + if (UnixMode) + return NativeErrorToSocketError.TryGetValue(error, out var err) + ? err + : SocketError.SocketError; + return (SocketError)error; + } + + public static SocketException GetSocketException() + { + int error = Marshal.GetLastWin32Error(); + if (UnixMode) + return NativeErrorToSocketError.TryGetValue(error, out var err) + ? new SocketException((int)err) + : new SocketException((int)SocketError.SocketError); + return new SocketException(error); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static short GetNativeAddressFamily(IPEndPoint remoteEndPoint) + { + return UnixMode + ? (short)(remoteEndPoint.AddressFamily == AddressFamily.InterNetwork ? AF_INET : AF_INET6) + : (short)remoteEndPoint.AddressFamily; + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NativeSocket.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NativeSocket.cs.meta new file mode 100644 index 0000000..f18fd7c --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NativeSocket.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 12cfcf4490f2b8f4db979ee833ecf5af +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetConstants.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetConstants.cs new file mode 100644 index 0000000..ca7dfbc --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetConstants.cs @@ -0,0 +1,75 @@ +namespace LiteNetLib +{ + /// + /// Sending method type + /// + public enum DeliveryMethod : byte + { + /// + /// Unreliable. Packets can be dropped, can be duplicated, can arrive without order. + /// + Unreliable = 4, + + /// + /// Reliable. Packets won't be dropped, won't be duplicated, can arrive without order. + /// + ReliableUnordered = 0, + + /// + /// Unreliable. Packets can be dropped, won't be duplicated, will arrive in order. + /// + Sequenced = 1, + + /// + /// Reliable and ordered. Packets won't be dropped, won't be duplicated, will arrive in order. + /// + ReliableOrdered = 2, + + /// + /// Reliable only last packet. Packets can be dropped (except the last one), won't be duplicated, will arrive in order. + /// Cannot be fragmented + /// + ReliableSequenced = 3 + } + + /// + /// Network constants. Can be tuned from sources for your purposes. + /// + public static class NetConstants + { + //can be tuned + public const int DefaultWindowSize = 64; + public const int SocketBufferSize = 1024 * 1024; //1mb + public const int SocketTTL = 255; + + public const int HeaderSize = 1; + public const int ChanneledHeaderSize = 4; + public const int FragmentHeaderSize = 6; + public const int FragmentedHeaderTotalSize = ChanneledHeaderSize + FragmentHeaderSize; + public const ushort MaxSequence = 32768; + public const ushort HalfMaxSequence = MaxSequence / 2; + + //protocol + internal const int ProtocolId = 13; + internal const int MaxUdpHeaderSize = 68; + internal const int ChannelTypeCount = 4; + + internal static readonly int[] PossibleMtu = + { + 576 - MaxUdpHeaderSize, //minimal (RFC 1191) + 1024, //most games standard + 1232 - MaxUdpHeaderSize, + 1460 - MaxUdpHeaderSize, //google cloud + 1472 - MaxUdpHeaderSize, //VPN + 1492 - MaxUdpHeaderSize, //Ethernet with LLC and SNAP, PPPoE (RFC 1042) + 1500 - MaxUdpHeaderSize //Ethernet II (RFC 1191) + }; + + //Max possible single packet size + public static readonly int MaxPacketSize = PossibleMtu[PossibleMtu.Length - 1]; + public static readonly int MaxUnreliableDataSize = MaxPacketSize - HeaderSize; + + //peer specific + public const byte MaxConnectionNumber = 4; + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetConstants.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetConstants.cs.meta new file mode 100644 index 0000000..653da3e --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetConstants.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b42ee5a523e67ff4c9149f91f7fe4245 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetDebug.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetDebug.cs new file mode 100644 index 0000000..a326c0d --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetDebug.cs @@ -0,0 +1,92 @@ +using System; +using System.Diagnostics; + +namespace LiteNetLib +{ + public class InvalidPacketException : ArgumentException + { + public InvalidPacketException(string message) : base(message) + { + } + } + + public class TooBigPacketException : InvalidPacketException + { + public TooBigPacketException(string message) : base(message) + { + } + } + + public enum NetLogLevel + { + Warning, + Error, + Trace, + Info + } + + /// + /// Interface to implement for your own logger + /// + public interface INetLogger + { + void WriteNet(NetLogLevel level, string str, params object[] args); + } + + /// + /// Static class for defining your own LiteNetLib logger instead of Console.WriteLine + /// or Debug.Log if compiled with UNITY flag + /// + public static class NetDebug + { + public static INetLogger Logger = null; + private static readonly object DebugLogLock = new object(); + private static void WriteLogic(NetLogLevel logLevel, string str, params object[] args) + { + lock (DebugLogLock) + { + if (Logger == null) + { +#if UNITY_5_3_OR_NEWER + UnityEngine.Debug.Log(string.Format(str, args)); +#else + Console.WriteLine(str, args); +#endif + } + else + { + Logger.WriteNet(logLevel, str, args); + } + } + } + + [Conditional("DEBUG_MESSAGES")] + internal static void Write(string str, params object[] args) + { + WriteLogic(NetLogLevel.Trace, str, args); + } + + [Conditional("DEBUG_MESSAGES")] + internal static void Write(NetLogLevel level, string str, params object[] args) + { + WriteLogic(level, str, args); + } + + [Conditional("DEBUG_MESSAGES"), Conditional("DEBUG")] + internal static void WriteForce(string str, params object[] args) + { + WriteLogic(NetLogLevel.Trace, str, args); + } + + [Conditional("DEBUG_MESSAGES"), Conditional("DEBUG")] + internal static void WriteForce(NetLogLevel level, string str, params object[] args) + { + WriteLogic(level, str, args); + } + + internal static void WriteError(string str, params object[] args) + { + WriteLogic(NetLogLevel.Error, str, args); + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetDebug.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetDebug.cs.meta new file mode 100644 index 0000000..73bd709 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetDebug.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9f50ae51c124bf1439e339eee1fcd6f5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.PacketPool.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.PacketPool.cs new file mode 100644 index 0000000..b2ae5e5 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.PacketPool.cs @@ -0,0 +1,80 @@ +using System; +using System.Threading; + +namespace LiteNetLib +{ + public partial class NetManager + { + private NetPacket _poolHead; + private int _poolCount; + private readonly object _poolLock = new object(); + + /// + /// Maximum packet pool size (increase if you have tons of packets sending) + /// + public int PacketPoolSize = 1000; + + public int PoolCount => _poolCount; + + private NetPacket PoolGetWithData(PacketProperty property, byte[] data, int start, int length) + { + int headerSize = NetPacket.GetHeaderSize(property); + NetPacket packet = PoolGetPacket(length + headerSize); + packet.Property = property; + Buffer.BlockCopy(data, start, packet.RawData, headerSize, length); + return packet; + } + + //Get packet with size + private NetPacket PoolGetWithProperty(PacketProperty property, int size) + { + NetPacket packet = PoolGetPacket(size + NetPacket.GetHeaderSize(property)); + packet.Property = property; + return packet; + } + + private NetPacket PoolGetWithProperty(PacketProperty property) + { + NetPacket packet = PoolGetPacket(NetPacket.GetHeaderSize(property)); + packet.Property = property; + return packet; + } + + internal NetPacket PoolGetPacket(int size) + { + NetPacket packet; + lock (_poolLock) + { + packet = _poolHead; + if (packet == null) + return new NetPacket(size); + + _poolHead = _poolHead.Next; + _poolCount--; + } + + packet.Size = size; + if (packet.RawData.Length < size) + packet.RawData = new byte[size]; + return packet; + } + + internal void PoolRecycle(NetPacket packet) + { + if (packet.RawData.Length > NetConstants.MaxPacketSize || _poolCount >= PacketPoolSize) + { + //Don't pool big packets. Save memory + return; + } + + //Clean fragmented flag + packet.RawData[0] = 0; + lock (_poolLock) + { + packet.Next = _poolHead; + _poolHead = packet; + _poolCount++; + } + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.PacketPool.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.PacketPool.cs.meta new file mode 100644 index 0000000..1927ff0 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.PacketPool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d384bada29340e542945f001cad348ac +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.Socket.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.Socket.cs new file mode 100644 index 0000000..6862b9c --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.Socket.cs @@ -0,0 +1,701 @@ +using System.Runtime.InteropServices; +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using LiteNetLib.Utils; + +namespace LiteNetLib +{ + + public partial class NetManager + { + public bool SocketActive(bool ipv4) + { + if (ipv4) + { + if (_udpSocketv4 != null) + return _udpSocketv4.Connected; + return false; + } + else + { + if (_udpSocketv6 != null) + return _udpSocketv6.Connected; + return false; + } + } + + private const int ReceivePollingTime = 500000; //0.5 second + + private Socket _udpSocketv4; + private Socket _udpSocketv6; + private Thread _threadv4; + private Thread _threadv6; + private IPEndPoint _bufferEndPointv4; + private IPEndPoint _bufferEndPointv6; + private PausedSocketFix _pausedSocketFix; + +#if !LITENETLIB_UNSAFE + [ThreadStatic] private static byte[] _sendToBuffer; +#endif + [ThreadStatic] private static byte[] _endPointBuffer; + + private readonly Dictionary _nativeAddrMap = new Dictionary(); + + private const int SioUdpConnreset = -1744830452; //SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12 + private static readonly IPAddress MulticastAddressV6 = IPAddress.Parse("ff02::1"); + public static readonly bool IPv6Support; + + /// + /// Maximum packets count that will be processed in Manual PollEvents + /// + public int MaxPacketsReceivePerUpdate = 0; + + public short Ttl + { + get + { +#if UNITY_SWITCH + return 0; +#else + return _udpSocketv4.Ttl; +#endif + } + internal set + { +#if !UNITY_SWITCH + _udpSocketv4.Ttl = value; +#endif + } + } + + static NetManager() + { +#if DISABLE_IPV6 + IPv6Support = false; +#elif !UNITY_2019_1_OR_NEWER && !UNITY_2018_4_OR_NEWER && (!UNITY_EDITOR && ENABLE_IL2CPP) + string version = UnityEngine.Application.unityVersion; + IPv6Support = Socket.OSSupportsIPv6 && int.Parse(version.Remove(version.IndexOf('f')).Split('.')[2]) >= 6; +#else + IPv6Support = Socket.OSSupportsIPv6; +#endif + } + + private bool IsActive() + { + return IsRunning; + } + + private void RegisterEndPoint(IPEndPoint ep) + { + if (UseNativeSockets && ep is NativeEndPoint nep) + { + _nativeAddrMap.Add(new NativeAddr(nep.NativeAddress, nep.NativeAddress.Length), nep); + } + } + + private void UnregisterEndPoint(IPEndPoint ep) + { + if (UseNativeSockets && ep is NativeEndPoint nep) + { + var nativeAddr = new NativeAddr(nep.NativeAddress, nep.NativeAddress.Length); + _nativeAddrMap.Remove(nativeAddr); + } + } + + private bool ProcessError(SocketException ex) + { + switch (ex.SocketErrorCode) + { +#if UNITY_IOS && !UNITY_EDITOR + case SocketError.NotConnected: +#endif + case SocketError.Interrupted: + case SocketError.NotSocket: + case SocketError.OperationAborted: + return true; + case SocketError.ConnectionReset: + case SocketError.MessageSize: + case SocketError.TimedOut: + case SocketError.NetworkReset: + //NetDebug.Write($"[R]Ignored error: {(int)ex.SocketErrorCode} - {ex}"); + break; + default: + NetDebug.WriteError($"[R]Error code: {(int)ex.SocketErrorCode} - {ex}"); + CreateEvent(NetEvent.EType.Error, errorCode: ex.SocketErrorCode); + break; + } + return false; + } + + private void ManualReceive(Socket socket, EndPoint bufferEndPoint) + { + //Reading data + try + { + int packetsReceived = 0; + while (socket.Available > 0) + { + var packet = PoolGetPacket(NetConstants.MaxPacketSize); + packet.Size = socket.ReceiveFrom(packet.RawData, 0, NetConstants.MaxPacketSize, SocketFlags.None, + ref bufferEndPoint); + //NetDebug.Write(NetLogLevel.Trace, $"[R]Received data from {bufferEndPoint}, result: {packet.Size}"); + OnMessageReceived(packet, (IPEndPoint)bufferEndPoint); + packetsReceived++; + if (packetsReceived == MaxPacketsReceivePerUpdate) + break; + } + } + catch (SocketException ex) + { + ProcessError(ex); + } + catch (ObjectDisposedException) + { + + } + catch (Exception e) + { + //protects socket receive thread + NetDebug.WriteError("[NM] SocketReceiveThread error: " + e); + } + } + + private void NativeReceiveLogic(object state) + { + Socket socket = (Socket)state; + IntPtr socketHandle = socket.Handle; + byte[] addrBuffer = new byte[socket.AddressFamily == AddressFamily.InterNetwork + ? NativeSocket.IPv4AddrSize + : NativeSocket.IPv6AddrSize]; + + int addrSize = addrBuffer.Length; + NetPacket packet = PoolGetPacket(NetConstants.MaxPacketSize); + + while (IsActive()) + { + //Reading data + packet.Size = NativeSocket.RecvFrom(socketHandle, packet.RawData, NetConstants.MaxPacketSize, addrBuffer, ref addrSize); + if (packet.Size == 0) + return; + if (packet.Size == -1) + { + SocketError errorCode = NativeSocket.GetSocketError(); + if (errorCode == SocketError.WouldBlock || errorCode == SocketError.TimedOut) //Linux timeout EAGAIN + continue; + if (ProcessError(new SocketException((int)errorCode))) + return; + continue; + } + + NativeAddr nativeAddr = new NativeAddr(addrBuffer, addrSize); + if (!_nativeAddrMap.TryGetValue(nativeAddr, out var endPoint)) + endPoint = new NativeEndPoint(addrBuffer); + + //All ok! + //NetDebug.WriteForce($"[R]Received data from {endPoint}, result: {packet.Size}"); + OnMessageReceived(packet, endPoint); + packet = PoolGetPacket(NetConstants.MaxPacketSize); + } + } + + private void ReceiveLogic(object state) + { + Socket socket = (Socket)state; + EndPoint bufferEndPoint = new IPEndPoint(socket.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, 0); + + while (IsActive()) + { + //Reading data + try + { + if (socket.Available == 0 && !socket.Poll(ReceivePollingTime, SelectMode.SelectRead)) + continue; + NetPacket packet = PoolGetPacket(NetConstants.MaxPacketSize); + packet.Size = socket.ReceiveFrom(packet.RawData, 0, NetConstants.MaxPacketSize, SocketFlags.None, + ref bufferEndPoint); + + //NetDebug.Write(NetLogLevel.Trace, $"[R]Received data from {bufferEndPoint}, result: {packet.Size}"); + OnMessageReceived(packet, (IPEndPoint)bufferEndPoint); + } + catch (SocketException ex) + { + if (ProcessError(ex)) + return; + } + catch (ObjectDisposedException) + { + //socket closed + return; + } + catch (ThreadAbortException) + { + //thread closed + return; + } + catch (Exception e) + { + //protects socket receive thread + NetDebug.WriteError("[NM] SocketReceiveThread error: " + e); + } + } + } + + /// + /// Start logic thread and listening on selected port + /// + /// bind to specific ipv4 address + /// bind to specific ipv6 address + /// port to listen + /// mode of library + public bool Start(IPAddress addressIPv4, IPAddress addressIPv6, int port, bool manualMode) + { + if (IsRunning && !IsActive()) + return false; + _manualMode = manualMode; + UseNativeSockets = UseNativeSockets && NativeSocket.IsSupported; + + //osx doesn't support dual mode + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && IPv6Mode == IPv6Mode.DualMode) + IPv6Mode = IPv6Mode.SeparateSocket; + + bool dualMode = IPv6Mode == IPv6Mode.DualMode && IPv6Support; + + _udpSocketv4 = new Socket( + dualMode ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, + SocketType.Dgram, + ProtocolType.Udp); + + if (!BindSocket(_udpSocketv4, new IPEndPoint(dualMode ? addressIPv6 : addressIPv4, port))) + return false; + + LocalPort = ((IPEndPoint)_udpSocketv4.LocalEndPoint).Port; + + if (_pausedSocketFix == null) + _pausedSocketFix = new PausedSocketFix(this, addressIPv4, addressIPv6, port, manualMode); + + if (dualMode) + _udpSocketv6 = _udpSocketv4; + + IsRunning = true; + if (!_manualMode) + { + ParameterizedThreadStart ts = ReceiveLogic; + if (UseNativeSockets) + ts = NativeReceiveLogic; + + _threadv4 = new Thread(ts) + { + Name = $"SocketThreadv4({LocalPort})", + IsBackground = true + }; + _threadv4.Start(_udpSocketv4); + + _logicThread = new Thread(UpdateLogic) { Name = "LogicThread", IsBackground = true }; + _logicThread.Start(); + } + else + { + _bufferEndPointv4 = new IPEndPoint(IPAddress.Any, 0); + } + + //Check IPv6 support + if (IPv6Support && IPv6Mode == IPv6Mode.SeparateSocket) + { + _udpSocketv6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); + //Use one port for two sockets + if (BindSocket(_udpSocketv6, new IPEndPoint(addressIPv6, LocalPort))) + { + if (_manualMode) + { + _bufferEndPointv6 = new IPEndPoint(IPAddress.IPv6Any, 0); + } + else + { + ParameterizedThreadStart ts = ReceiveLogic; + if (UseNativeSockets) + ts = NativeReceiveLogic; + _threadv6 = new Thread(ts) + { + Name = $"SocketThreadv6({LocalPort})", + IsBackground = true + }; + _threadv6.Start(_udpSocketv6); + } + } + } + + return true; + } + + private bool BindSocket(Socket socket, IPEndPoint ep) + { + //Setup socket + socket.ReceiveTimeout = 500; + socket.SendTimeout = 500; + socket.ReceiveBufferSize = NetConstants.SocketBufferSize; + socket.SendBufferSize = NetConstants.SocketBufferSize; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + try + { + socket.IOControl(SioUdpConnreset, new byte[] { 0 }, null); + } + catch + { + //ignored + } + } + + try + { + socket.ExclusiveAddressUse = !ReuseAddress; + socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, ReuseAddress); + } + catch + { + //Unity with IL2CPP throws an exception here, it doesn't matter in most cases so just ignore it + } + if (ep.AddressFamily == AddressFamily.InterNetwork || IPv6Mode == IPv6Mode.DualMode) + { + Ttl = NetConstants.SocketTTL; + + try { socket.EnableBroadcast = true; } + catch (SocketException e) + { + NetDebug.WriteError($"[B]Broadcast error: {e.SocketErrorCode}"); + } + + if (IPv6Mode == IPv6Mode.DualMode) + { + try { socket.DualMode = true; } + catch (Exception e) + { + NetDebug.WriteError($"[B]Bind exception (dualmode setting): {e}"); + } + } + else if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + try { socket.DontFragment = true; } + catch (SocketException e) + { + NetDebug.WriteError($"[B]DontFragment error: {e.SocketErrorCode}"); + } + } + } + //Bind + try + { + socket.Bind(ep); + NetDebug.Write(NetLogLevel.Trace, $"[B]Successfully binded to port: {((IPEndPoint)socket.LocalEndPoint).Port}, AF: {socket.AddressFamily}"); + + //join multicast + if (ep.AddressFamily == AddressFamily.InterNetworkV6) + { + try + { +#if !UNITY_2018_3_OR_NEWER + socket.SetSocketOption( + SocketOptionLevel.IPv6, + SocketOptionName.AddMembership, + new IPv6MulticastOption(MulticastAddressV6)); +#endif + } + catch (Exception) + { + // Unity3d throws exception - ignored + } + } + } + catch (SocketException bindException) + { + switch (bindException.SocketErrorCode) + { + //IPv6 bind fix + case SocketError.AddressAlreadyInUse: + if (socket.AddressFamily == AddressFamily.InterNetworkV6 && IPv6Mode != IPv6Mode.DualMode) + { + try + { + //Set IPv6Only + socket.DualMode = false; + socket.Bind(ep); + } + catch (SocketException ex) + { + //because its fixed in 2018_3 + NetDebug.WriteError($"[B]Bind exception: {ex}, errorCode: {ex.SocketErrorCode}"); + return false; + } + return true; + } + break; + //hack for iOS (Unity3D) + case SocketError.AddressFamilyNotSupported: + return true; + } + NetDebug.WriteError($"[B]Bind exception: {bindException}, errorCode: {bindException.SocketErrorCode}"); + return false; + } + return true; + } + + internal int SendRawAndRecycle(NetPacket packet, IPEndPoint remoteEndPoint) + { + int result = SendRaw(packet.RawData, 0, packet.Size, remoteEndPoint); + PoolRecycle(packet); + return result; + } + + internal int SendRaw(NetPacket packet, IPEndPoint remoteEndPoint) + { + return SendRaw(packet.RawData, 0, packet.Size, remoteEndPoint); + } + + internal int SendRaw(byte[] message, int start, int length, IPEndPoint remoteEndPoint) + { + if (!IsRunning) + return 0; + + NetPacket expandedPacket = null; + if (_extraPacketLayer != null) + { + expandedPacket = PoolGetPacket(length + _extraPacketLayer.ExtraPacketSizeForLayer); + Buffer.BlockCopy(message, start, expandedPacket.RawData, 0, length); + start = 0; + _extraPacketLayer.ProcessOutBoundPacket(ref remoteEndPoint, ref expandedPacket.RawData, ref start, ref length); + message = expandedPacket.RawData; + } + + var socket = _udpSocketv4; + if (remoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6 && IPv6Support) + { + socket = _udpSocketv6; + if (socket == null) + return 0; + } + + int result; + try + { + if (UseNativeSockets) + { + byte[] socketAddress; + + if (remoteEndPoint is NativeEndPoint nep) + { + socketAddress = nep.NativeAddress; + } + else //Convert endpoint to raw + { + if (_endPointBuffer == null) + _endPointBuffer = new byte[NativeSocket.IPv6AddrSize]; + socketAddress = _endPointBuffer; + + bool ipv4 = remoteEndPoint.AddressFamily == AddressFamily.InterNetwork; + short addressFamily = NativeSocket.GetNativeAddressFamily(remoteEndPoint); + + socketAddress[0] = (byte)(addressFamily); + socketAddress[1] = (byte)(addressFamily >> 8); + socketAddress[2] = (byte)(remoteEndPoint.Port >> 8); + socketAddress[3] = (byte)(remoteEndPoint.Port); + + if (ipv4) + { +#pragma warning disable 618 + long addr = remoteEndPoint.Address.Address; +#pragma warning restore 618 + socketAddress[4] = (byte)(addr); + socketAddress[5] = (byte)(addr >> 8); + socketAddress[6] = (byte)(addr >> 16); + socketAddress[7] = (byte)(addr >> 24); + } + else + { +#if NETCOREAPP || NETSTANDARD2_1 || NETSTANDARD2_1_OR_GREATER + remoteEndPoint.Address.TryWriteBytes(new Span(socketAddress, 8, 16), out _); +#else + byte[] addrBytes = remoteEndPoint.Address.GetAddressBytes(); + Buffer.BlockCopy(addrBytes, 0, socketAddress, 8, 16); +#endif + } + } + +#if LITENETLIB_UNSAFE + unsafe + { + fixed (byte* dataWithOffset = &message[start]) + { + result = + NativeSocket.SendTo(socket.Handle, dataWithOffset, length, socketAddress, socketAddress.Length); + } + } +#else + if (start > 0) + { + if (_sendToBuffer == null) + _sendToBuffer = new byte[NetConstants.MaxPacketSize]; + Buffer.BlockCopy(message, start, _sendToBuffer, 0, length); + message = _sendToBuffer; + } + + result = NativeSocket.SendTo(socket.Handle, message, length, socketAddress, socketAddress.Length); +#endif + if (result == -1) + throw NativeSocket.GetSocketException(); + } + else + { + result = socket.SendTo(message, start, length, SocketFlags.None, remoteEndPoint); + } + //NetDebug.WriteForce("[S]Send packet to {0}, result: {1}", remoteEndPoint, result); + } + catch (SocketException ex) + { + switch (ex.SocketErrorCode) + { + case SocketError.NoBufferSpaceAvailable: + case SocketError.Interrupted: + return 0; + case SocketError.MessageSize: + NetDebug.Write(NetLogLevel.Trace, "[SRD] 10040, datalen: {0}", length); + return 0; + + case SocketError.HostUnreachable: + case SocketError.NetworkUnreachable: + if (DisconnectOnUnreachable && TryGetPeer(remoteEndPoint, out var fromPeer)) + { + DisconnectPeerForce( + fromPeer, + ex.SocketErrorCode == SocketError.HostUnreachable + ? DisconnectReason.HostUnreachable + : DisconnectReason.NetworkUnreachable, + ex.SocketErrorCode, + null); + } + + CreateEvent(NetEvent.EType.Error, remoteEndPoint: remoteEndPoint, errorCode: ex.SocketErrorCode); + return -1; + + default: + NetDebug.WriteError($"[S] {ex}"); + return -1; + } + } + catch (Exception ex) + { + NetDebug.WriteError($"[S] {ex}"); + return 0; + } + finally + { + if (expandedPacket != null) + { + PoolRecycle(expandedPacket); + } + } + + if (result <= 0) + return 0; + + if (EnableStatistics) + { + Statistics.IncrementPacketsSent(); + Statistics.AddBytesSent(length); + } + + return result; + } + + public bool SendBroadcast(NetDataWriter writer, int port) + { + return SendBroadcast(writer.Data, 0, writer.Length, port); + } + + public bool SendBroadcast(byte[] data, int port) + { + return SendBroadcast(data, 0, data.Length, port); + } + + public bool SendBroadcast(byte[] data, int start, int length, int port) + { + if (!IsActive()) + return false; + + NetPacket packet; + if (_extraPacketLayer != null) + { + var headerSize = NetPacket.GetHeaderSize(PacketProperty.Broadcast); + packet = PoolGetPacket(headerSize + length + _extraPacketLayer.ExtraPacketSizeForLayer); + packet.Property = PacketProperty.Broadcast; + Buffer.BlockCopy(data, start, packet.RawData, headerSize, length); + var checksumComputeStart = 0; + int preCrcLength = length + headerSize; + IPEndPoint emptyEp = null; + _extraPacketLayer.ProcessOutBoundPacket(ref emptyEp, ref packet.RawData, ref checksumComputeStart, ref preCrcLength); + } + else + { + packet = PoolGetWithData(PacketProperty.Broadcast, data, start, length); + } + + bool broadcastSuccess = false; + bool multicastSuccess = false; + try + { + broadcastSuccess = _udpSocketv4.SendTo( + packet.RawData, + 0, + packet.Size, + SocketFlags.None, + new IPEndPoint(IPAddress.Broadcast, port)) > 0; + + if (_udpSocketv6 != null) + { + multicastSuccess = _udpSocketv6.SendTo( + packet.RawData, + 0, + packet.Size, + SocketFlags.None, + new IPEndPoint(MulticastAddressV6, port)) > 0; + } + } + catch (Exception ex) + { + NetDebug.WriteError($"[S][MCAST] {ex}"); + return broadcastSuccess; + } + finally + { + PoolRecycle(packet); + } + + return broadcastSuccess || multicastSuccess; + } + + internal void CloseSocket(bool suspend) + { + if (!suspend) + IsRunning = false; + + //cleanup dual mode + if (_udpSocketv4 == _udpSocketv6) + _udpSocketv6 = null; + + _udpSocketv4?.Close(); + _udpSocketv6?.Close(); + _udpSocketv4 = null; + _udpSocketv6 = null; + + if (_threadv4 != null && _threadv4 != Thread.CurrentThread) + _threadv4.Join(); + if (_threadv6 != null && _threadv6 != Thread.CurrentThread) + _threadv6.Join(); + _threadv4 = null; + _threadv6 = null; + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.Socket.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.Socket.cs.meta new file mode 100644 index 0000000..a418311 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.Socket.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9cc0423407df3e74a8e5a15dbbad2598 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.cs new file mode 100644 index 0000000..23ee56b --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.cs @@ -0,0 +1,1789 @@ +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using LiteNetLib.Layers; +using LiteNetLib.Utils; + +namespace LiteNetLib +{ + public enum IPv6Mode + { + Disabled, + SeparateSocket, + DualMode + } + + public sealed class NetPacketReader : NetDataReader + { + private NetPacket _packet; + private readonly NetManager _manager; + private readonly NetEvent _evt; + + internal NetPacketReader(NetManager manager, NetEvent evt) + { + _manager = manager; + _evt = evt; + } + + internal void SetSource(NetPacket packet, int headerSize) + { + if (packet == null) + return; + _packet = packet; + SetSource(packet.RawData, headerSize, packet.Size); + } + + internal void Recycle_Internal() + { + Clear(); + if (_packet != null) + _manager.PoolRecycle(_packet); + _packet = null; + _manager.RecycleEvent(_evt); + } + + public void Recycle() + { + if (_manager.AutoRecycle) + return; + Recycle_Internal(); + } + } + + internal sealed class NetEvent + { + public NetEvent Next; + + public enum EType + { + Connect, + Disconnect, + Receive, + ReceiveUnconnected, + Error, + ConnectionLatencyUpdated, + Broadcast, + ConnectionRequest, + MessageDelivered, + PeerAddressChanged + } + public EType Type; + + public NetPeer Peer; + public IPEndPoint RemoteEndPoint; + public object UserData; + public int Latency; + public SocketError ErrorCode; + public DisconnectReason DisconnectReason; + public ConnectionRequest ConnectionRequest; + public DeliveryMethod DeliveryMethod; + public byte ChannelNumber; + public readonly NetPacketReader DataReader; + + public NetEvent(NetManager manager) + { + DataReader = new NetPacketReader(manager, this); + } + } + + /// + /// Main class for all network operations. Can be used as client and/or server. + /// + public partial class NetManager : IEnumerable + { + private class IPEndPointComparer : IEqualityComparer + { + public bool Equals(IPEndPoint x, IPEndPoint y) + { + return x.Address.Equals(y.Address) && x.Port == y.Port; + } + + public int GetHashCode(IPEndPoint obj) + { + return obj.GetHashCode(); + } + } + + public struct NetPeerEnumerator : IEnumerator + { + private readonly NetPeer _initialPeer; + private NetPeer _p; + + public NetPeerEnumerator(NetPeer p) + { + _initialPeer = p; + _p = null; + } + + public void Dispose() + { + + } + + public bool MoveNext() + { + _p = _p == null ? _initialPeer : _p.NextPeer; + return _p != null; + } + + public void Reset() + { + throw new NotSupportedException(); + } + + public NetPeer Current => _p; + object IEnumerator.Current => _p; + } + +#if DEBUG + private struct IncomingData + { + public NetPacket Data; + public IPEndPoint EndPoint; + public DateTime TimeWhenGet; + } + private readonly List _pingSimulationList = new List(); + private readonly Random _randomGenerator = new Random(); + private const int MinLatencyThreshold = 5; +#endif + + private Thread _logicThread; + private bool _manualMode; + private readonly AutoResetEvent _updateTriggerEvent = new AutoResetEvent(true); + + private Queue _netEventsProduceQueue = new Queue(); + private Queue _netEventsConsumeQueue = new Queue(); + + private NetEvent _netEventPoolHead; + private readonly INetEventListener _netEventListener; + private readonly IDeliveryEventListener _deliveryEventListener; + private readonly INtpEventListener _ntpEventListener; + private readonly IPeerAddressChangedListener _peerAddressChangedListener; + + private readonly Dictionary _peersDict = new Dictionary(new IPEndPointComparer()); + private readonly Dictionary _requestsDict = new Dictionary(new IPEndPointComparer()); + private readonly Dictionary _ntpRequests = new Dictionary(new IPEndPointComparer()); + private readonly ReaderWriterLockSlim _peersLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); + private volatile NetPeer _headPeer; + private int _connectedPeersCount; + private readonly List _connectedPeerListCache = new List(); + private NetPeer[] _peersArray = new NetPeer[32]; + private readonly PacketLayerBase _extraPacketLayer; + private int _lastPeerId; + private ConcurrentQueue _peerIds = new ConcurrentQueue(); + private byte _channelsCount = 1; + private readonly object _eventLock = new object(); + + //config section + /// + /// Enable messages receiving without connection. (with SendUnconnectedMessage method) + /// + public bool UnconnectedMessagesEnabled = false; + + /// + /// Enable nat punch messages + /// + public bool NatPunchEnabled = false; + + /// + /// Library logic update and send period in milliseconds + /// Lowest values in Windows doesn't change much because of Thread.Sleep precision + /// To more frequent sends (or sends tied to your game logic) use + /// + public int UpdateTime = 15; + + /// + /// Interval for latency detection and checking connection (in milliseconds) + /// + public int PingInterval = 1000; + + /// + /// If NetManager doesn't receive any packet from remote peer during this time (in milliseconds) then connection will be closed + /// (including library internal keepalive packets) + /// + public int DisconnectTimeout = 5000; + + /// + /// Simulate packet loss by dropping random amount of packets. (Works only in DEBUG mode) + /// + public bool SimulatePacketLoss = false; + + /// + /// Simulate latency by holding packets for random time. (Works only in DEBUG mode) + /// + public bool SimulateLatency = false; + + /// + /// Chance of packet loss when simulation enabled. value in percents (1 - 100). + /// + public int SimulationPacketLossChance = 10; + + /// + /// Minimum simulated latency (in milliseconds) + /// + public int SimulationMinLatency = 30; + + /// + /// Maximum simulated latency (in milliseconds) + /// + public int SimulationMaxLatency = 100; + + /// + /// Events automatically will be called without PollEvents method from another thread + /// + public bool UnsyncedEvents = false; + + /// + /// If true - receive event will be called from "receive" thread immediately otherwise on PollEvents call + /// + public bool UnsyncedReceiveEvent = false; + + /// + /// If true - delivery event will be called from "receive" thread immediately otherwise on PollEvents call + /// + public bool UnsyncedDeliveryEvent = false; + + /// + /// Allows receive broadcast packets + /// + public bool BroadcastReceiveEnabled = false; + + /// + /// Delay between initial connection attempts (in milliseconds) + /// + public int ReconnectDelay = 500; + + /// + /// Maximum connection attempts before client stops and call disconnect event. + /// + public int MaxConnectAttempts = 10; + + /// + /// Enables socket option "ReuseAddress" for specific purposes + /// + public bool ReuseAddress = false; + + /// + /// Statistics of all connections + /// + public readonly NetStatistics Statistics = new NetStatistics(); + + /// + /// Toggles the collection of network statistics for the instance and all known peers + /// + public bool EnableStatistics = false; + + /// + /// NatPunchModule for NAT hole punching operations + /// + public readonly NatPunchModule NatPunchModule; + + /// + /// Returns true if socket listening and update thread is running + /// + public bool IsRunning { get; private set; } + + /// + /// Local EndPoint (host and port) + /// + public int LocalPort { get; private set; } + + /// + /// Automatically recycle NetPacketReader after OnReceive event + /// + public bool AutoRecycle; + + /// + /// IPv6 support + /// + public IPv6Mode IPv6Mode = IPv6Mode.SeparateSocket; + + /// + /// Override MTU for all new peers registered in this NetManager, will ignores MTU Discovery! + /// + public int MtuOverride = 0; + + /// + /// Sets initial MTU to lowest possible value according to RFC1191 (576 bytes) + /// + public bool UseSafeMtu = false; + + /// + /// First peer. Useful for Client mode + /// + public NetPeer FirstPeer => _headPeer; + + /// + /// Experimental feature mostly for servers. Only for Windows/Linux + /// use direct socket calls for send/receive to drastically increase speed and reduce GC pressure + /// + public bool UseNativeSockets = false; + + /// + /// Disconnect peers if HostUnreachable or NetworkUnreachable spawned (old behaviour 0.9.x was true) + /// + public bool DisconnectOnUnreachable = false; + + /// + /// Allows peer change it's ip (lte to wifi, wifi to lte, etc). Use only on server + /// + public bool AllowPeerAddressChange = false; + + /// + /// QoS channel count per message type (value must be between 1 and 64 channels) + /// + public byte ChannelsCount + { + get => _channelsCount; + set + { + if (value < 1 || value > 64) + throw new ArgumentException("Channels count must be between 1 and 64"); + _channelsCount = value; + } + } + + /// + /// Returns connected peers list (with internal cached list) + /// + public List ConnectedPeerList + { + get + { + GetPeersNonAlloc(_connectedPeerListCache, ConnectionState.Connected); + return _connectedPeerListCache; + } + } + + /// + /// Gets peer by peer id + /// + /// id of peer + /// Peer if peer with id exist, otherwise null + public NetPeer GetPeerById(int id) + { + if (id >= 0 && id < _peersArray.Length) + { + return _peersArray[id]; + } + + return null; + } + + /// + /// Gets peer by peer id + /// + /// id of peer + /// resulting peer + /// True if peer with id exist, otherwise false + public bool TryGetPeerById(int id, out NetPeer peer) + { + peer = GetPeerById(id); + + return peer != null; + } + + /// + /// Returns connected peers count + /// + public int ConnectedPeersCount => Interlocked.CompareExchange(ref _connectedPeersCount,0,0); + + public int ExtraPacketSizeForLayer => _extraPacketLayer?.ExtraPacketSizeForLayer ?? 0; + + private bool TryGetPeer(IPEndPoint endPoint, out NetPeer peer) + { + _peersLock.EnterReadLock(); + bool result = _peersDict.TryGetValue(endPoint, out peer); + _peersLock.ExitReadLock(); + return result; + } + + private void AddPeer(NetPeer peer) + { + _peersLock.EnterWriteLock(); + if (_headPeer != null) + { + peer.NextPeer = _headPeer; + _headPeer.PrevPeer = peer; + } + _headPeer = peer; + _peersDict.Add(peer.EndPoint, peer); + if (peer.Id >= _peersArray.Length) + { + int newSize = _peersArray.Length * 2; + while (peer.Id >= newSize) + newSize *= 2; + Array.Resize(ref _peersArray, newSize); + } + _peersArray[peer.Id] = peer; + RegisterEndPoint(peer.EndPoint); + _peersLock.ExitWriteLock(); + } + + private void RemovePeer(NetPeer peer) + { + _peersLock.EnterWriteLock(); + RemovePeer_Internal(peer); + _peersLock.ExitWriteLock(); + } + + private void RemovePeer_Internal(NetPeer peer) + { + if (!_peersDict.Remove(peer.EndPoint)) + return; + if (peer == _headPeer) + _headPeer = peer.NextPeer; + + if (peer.PrevPeer != null) + peer.PrevPeer.NextPeer = peer.NextPeer; + if (peer.NextPeer != null) + peer.NextPeer.PrevPeer = peer.PrevPeer; + peer.PrevPeer = null; + + _peersArray[peer.Id] = null; + _peerIds.Enqueue(peer.Id); + UnregisterEndPoint(peer.EndPoint); + } + + /// + /// NetManager constructor + /// + /// Network events listener (also can implement IDeliveryEventListener) + /// Extra processing of packages, like CRC checksum or encryption. All connected NetManagers must have same layer. + public NetManager(INetEventListener listener, PacketLayerBase extraPacketLayer = null) + { + _netEventListener = listener; + _deliveryEventListener = listener as IDeliveryEventListener; + _ntpEventListener = listener as INtpEventListener; + _peerAddressChangedListener = listener as IPeerAddressChangedListener; + NatPunchModule = new NatPunchModule(this); + _extraPacketLayer = extraPacketLayer; + } + + internal void ConnectionLatencyUpdated(NetPeer fromPeer, int latency) + { + CreateEvent(NetEvent.EType.ConnectionLatencyUpdated, fromPeer, latency: latency); + } + + internal void MessageDelivered(NetPeer fromPeer, object userData) + { + if(_deliveryEventListener != null) + CreateEvent(NetEvent.EType.MessageDelivered, fromPeer, userData: userData); + } + + internal void DisconnectPeerForce(NetPeer peer, + DisconnectReason reason, + SocketError socketErrorCode, + NetPacket eventData) + { + DisconnectPeer(peer, reason, socketErrorCode, true, null, 0, 0, eventData); + } + + private void DisconnectPeer( + NetPeer peer, + DisconnectReason reason, + SocketError socketErrorCode, + bool force, + byte[] data, + int start, + int count, + NetPacket eventData) + { + var shutdownResult = peer.Shutdown(data, start, count, force); + if (shutdownResult == ShutdownResult.None) + return; + if(shutdownResult == ShutdownResult.WasConnected) + Interlocked.Decrement(ref _connectedPeersCount); + CreateEvent( + NetEvent.EType.Disconnect, + peer, + errorCode: socketErrorCode, + disconnectReason: reason, + readerSource: eventData); + } + + private void CreateEvent( + NetEvent.EType type, + NetPeer peer = null, + IPEndPoint remoteEndPoint = null, + SocketError errorCode = 0, + int latency = 0, + DisconnectReason disconnectReason = DisconnectReason.ConnectionFailed, + ConnectionRequest connectionRequest = null, + DeliveryMethod deliveryMethod = DeliveryMethod.Unreliable, + byte channelNumber = 0, + NetPacket readerSource = null, + object userData = null) + { + NetEvent evt; + bool unsyncEvent = UnsyncedEvents; + + if (type == NetEvent.EType.Connect) + Interlocked.Increment(ref _connectedPeersCount); + else if (type == NetEvent.EType.MessageDelivered) + unsyncEvent = UnsyncedDeliveryEvent; + + lock(_eventLock) + { + evt = _netEventPoolHead; + if (evt == null) + evt = new NetEvent(this); + else + _netEventPoolHead = evt.Next; + } + + evt.Type = type; + evt.DataReader.SetSource(readerSource, readerSource?.GetHeaderSize() ?? 0); + evt.Peer = peer; + evt.RemoteEndPoint = remoteEndPoint; + evt.Latency = latency; + evt.ErrorCode = errorCode; + evt.DisconnectReason = disconnectReason; + evt.ConnectionRequest = connectionRequest; + evt.DeliveryMethod = deliveryMethod; + evt.ChannelNumber = channelNumber; + evt.UserData = userData; + + if (unsyncEvent || _manualMode) + { + ProcessEvent(evt); + } + else + { + lock(_netEventsProduceQueue) + _netEventsProduceQueue.Enqueue(evt); + } + } + + private void ProcessEvent(NetEvent evt) + { + NetDebug.Write("[NM] Processing event: " + evt.Type); + bool emptyData = evt.DataReader.IsNull; + switch (evt.Type) + { + case NetEvent.EType.Connect: + _netEventListener.OnPeerConnected(evt.Peer); + break; + case NetEvent.EType.Disconnect: + var info = new DisconnectInfo + { + Reason = evt.DisconnectReason, + AdditionalData = evt.DataReader, + SocketErrorCode = evt.ErrorCode + }; + _netEventListener.OnPeerDisconnected(evt.Peer, info); + break; + case NetEvent.EType.Receive: + _netEventListener.OnNetworkReceive(evt.Peer, evt.DataReader, evt.ChannelNumber, evt.DeliveryMethod); + break; + case NetEvent.EType.ReceiveUnconnected: + _netEventListener.OnNetworkReceiveUnconnected(evt.RemoteEndPoint, evt.DataReader, UnconnectedMessageType.BasicMessage); + break; + case NetEvent.EType.Broadcast: + _netEventListener.OnNetworkReceiveUnconnected(evt.RemoteEndPoint, evt.DataReader, UnconnectedMessageType.Broadcast); + break; + case NetEvent.EType.Error: + _netEventListener.OnNetworkError(evt.RemoteEndPoint, evt.ErrorCode); + break; + case NetEvent.EType.ConnectionLatencyUpdated: + _netEventListener.OnNetworkLatencyUpdate(evt.Peer, evt.Latency); + break; + case NetEvent.EType.ConnectionRequest: + _netEventListener.OnConnectionRequest(evt.ConnectionRequest); + break; + case NetEvent.EType.MessageDelivered: + _deliveryEventListener.OnMessageDelivered(evt.Peer, evt.UserData); + break; + case NetEvent.EType.PeerAddressChanged: + _peersLock.EnterUpgradeableReadLock(); + IPEndPoint previousAddress = null; + if (_peersDict.ContainsKey(evt.Peer.EndPoint)) + { + _peersLock.EnterWriteLock(); + _peersDict.Remove(evt.Peer.EndPoint); + previousAddress = evt.Peer.EndPoint; + evt.Peer.FinishEndPointChange(evt.RemoteEndPoint); + _peersDict.Add(evt.Peer.EndPoint, evt.Peer); + _peersLock.ExitWriteLock(); + } + _peersLock.ExitUpgradeableReadLock(); + if(previousAddress != null) + _peerAddressChangedListener.OnPeerAddressChanged(evt.Peer, previousAddress); + break; + } + //Recycle if not message + if (emptyData) + RecycleEvent(evt); + else if (AutoRecycle) + evt.DataReader.Recycle_Internal(); + } + + internal void RecycleEvent(NetEvent evt) + { + evt.Peer = null; + evt.ErrorCode = 0; + evt.RemoteEndPoint = null; + evt.ConnectionRequest = null; + lock(_eventLock) + { + evt.Next = _netEventPoolHead; + _netEventPoolHead = evt; + } + } + + //Update function + private void UpdateLogic() + { + var peersToRemove = new List(); + var stopwatch = new Stopwatch(); + stopwatch.Start(); + + while (IsRunning) + { + try + { + ProcessDelayedPackets(); + int elapsed = (int)stopwatch.ElapsedMilliseconds; + elapsed = elapsed <= 0 ? 1 : elapsed; + stopwatch.Restart(); + + for (var netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) + { + if (netPeer.ConnectionState == ConnectionState.Disconnected && + netPeer.TimeSinceLastPacket > DisconnectTimeout) + { + peersToRemove.Add(netPeer); + } + else + { + netPeer.Update(elapsed); + } + } + + if (peersToRemove.Count > 0) + { + _peersLock.EnterWriteLock(); + for (int i = 0; i < peersToRemove.Count; i++) + RemovePeer_Internal(peersToRemove[i]); + _peersLock.ExitWriteLock(); + peersToRemove.Clear(); + } + + ProcessNtpRequests(elapsed); + + int sleepTime = UpdateTime - (int)stopwatch.ElapsedMilliseconds; + if (sleepTime > 0) + _updateTriggerEvent.WaitOne(sleepTime); + } + catch (ThreadAbortException) + { + return; + } + catch (Exception e) + { + NetDebug.WriteError("[NM] LogicThread error: " + e); + } + } + stopwatch.Stop(); + } + + [Conditional("DEBUG")] + private void ProcessDelayedPackets() + { +#if DEBUG + if (!SimulateLatency) + return; + + var time = DateTime.UtcNow; + lock (_pingSimulationList) + { + for (int i = 0; i < _pingSimulationList.Count; i++) + { + var incomingData = _pingSimulationList[i]; + if (incomingData.TimeWhenGet <= time) + { + DebugMessageReceived(incomingData.Data, incomingData.EndPoint); + _pingSimulationList.RemoveAt(i); + i--; + } + } + } +#endif + } + + private void ProcessNtpRequests(int elapsedMilliseconds) + { + List requestsToRemove = null; + foreach (var ntpRequest in _ntpRequests) + { + ntpRequest.Value.Send(_udpSocketv4, elapsedMilliseconds); + if(ntpRequest.Value.NeedToKill) + { + if (requestsToRemove == null) + requestsToRemove = new List(); + requestsToRemove.Add(ntpRequest.Key); + } + } + + if (requestsToRemove != null) + { + foreach (var ipEndPoint in requestsToRemove) + { + _ntpRequests.Remove(ipEndPoint); + } + } + } + + /// + /// Update and send logic. Use this only when NetManager started in manual mode + /// + /// elapsed milliseconds since last update call + public void ManualUpdate(int elapsedMilliseconds) + { + if (!_manualMode) + return; + + for (var netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) + { + if (netPeer.ConnectionState == ConnectionState.Disconnected && netPeer.TimeSinceLastPacket > DisconnectTimeout) + { + RemovePeer_Internal(netPeer); + } + else + { + netPeer.Update(elapsedMilliseconds); + } + } + ProcessNtpRequests(elapsedMilliseconds); + } + + internal NetPeer OnConnectionSolved(ConnectionRequest request, byte[] rejectData, int start, int length) + { + NetPeer netPeer = null; + + if (request.Result == ConnectionRequestResult.RejectForce) + { + NetDebug.Write(NetLogLevel.Trace, "[NM] Peer connect reject force."); + if (rejectData != null && length > 0) + { + var shutdownPacket = PoolGetWithProperty(PacketProperty.Disconnect, length); + shutdownPacket.ConnectionNumber = request.InternalPacket.ConnectionNumber; + FastBitConverter.GetBytes(shutdownPacket.RawData, 1, request.InternalPacket.ConnectionTime); + if (shutdownPacket.Size >= NetConstants.PossibleMtu[0]) + NetDebug.WriteError("[Peer] Disconnect additional data size more than MTU!"); + else + Buffer.BlockCopy(rejectData, start, shutdownPacket.RawData, 9, length); + SendRawAndRecycle(shutdownPacket, request.RemoteEndPoint); + } + } + else + { + _peersLock.EnterUpgradeableReadLock(); + if (_peersDict.TryGetValue(request.RemoteEndPoint, out netPeer)) + { + //already have peer + _peersLock.ExitUpgradeableReadLock(); + } + else if (request.Result == ConnectionRequestResult.Reject) + { + netPeer = new NetPeer(this, request.RemoteEndPoint, GetNextPeerId()); + netPeer.Reject(request.InternalPacket, rejectData, start, length); + AddPeer(netPeer); + _peersLock.ExitUpgradeableReadLock(); + NetDebug.Write(NetLogLevel.Trace, "[NM] Peer connect reject."); + } + else //Accept + { + netPeer = new NetPeer(this, request, GetNextPeerId()); + AddPeer(netPeer); + _peersLock.ExitUpgradeableReadLock(); + CreateEvent(NetEvent.EType.Connect, netPeer); + NetDebug.Write(NetLogLevel.Trace, "[NM] Received peer connection Id: {0}, EP: {1}", + netPeer.ConnectTime, netPeer.EndPoint); + } + } + + lock(_requestsDict) + _requestsDict.Remove(request.RemoteEndPoint); + + return netPeer; + } + + private int GetNextPeerId() + { + return _peerIds.TryDequeue(out int id) ? id : _lastPeerId++; + } + + private void ProcessConnectRequest( + IPEndPoint remoteEndPoint, + NetPeer netPeer, + NetConnectRequestPacket connRequest) + { + //if we have peer + if (netPeer != null) + { + var processResult = netPeer.ProcessConnectRequest(connRequest); + NetDebug.Write("ConnectRequest LastId: {0}, NewId: {1}, EP: {2}, Result: {3}", + netPeer.ConnectTime, + connRequest.ConnectionTime, + remoteEndPoint, + processResult); + + switch (processResult) + { + case ConnectRequestResult.Reconnection: + DisconnectPeerForce(netPeer, DisconnectReason.Reconnect, 0, null); + RemovePeer(netPeer); + //go to new connection + break; + case ConnectRequestResult.NewConnection: + RemovePeer(netPeer); + //go to new connection + break; + case ConnectRequestResult.P2PLose: + DisconnectPeerForce(netPeer, DisconnectReason.PeerToPeerConnection, 0, null); + RemovePeer(netPeer); + //go to new connection + break; + default: + //no operations needed + return; + } + //ConnectRequestResult.NewConnection + //Set next connection number + if(processResult != ConnectRequestResult.P2PLose) + connRequest.ConnectionNumber = (byte)((netPeer.ConnectionNum + 1) % NetConstants.MaxConnectionNumber); + //To reconnect peer + } + else + { + NetDebug.Write("ConnectRequest Id: {0}, EP: {1}", connRequest.ConnectionTime, remoteEndPoint); + } + + ConnectionRequest req; + lock (_requestsDict) + { + if (_requestsDict.TryGetValue(remoteEndPoint, out req)) + { + req.UpdateRequest(connRequest); + return; + } + req = new ConnectionRequest(remoteEndPoint, connRequest, this); + _requestsDict.Add(remoteEndPoint, req); + } + NetDebug.Write("[NM] Creating request event: " + connRequest.ConnectionTime); + CreateEvent(NetEvent.EType.ConnectionRequest, connectionRequest: req); + } + + private void OnMessageReceived(NetPacket packet, IPEndPoint remoteEndPoint) + { +#if DEBUG + if (SimulatePacketLoss && _randomGenerator.NextDouble() * 100 < SimulationPacketLossChance) + { + //drop packet + return; + } + if (SimulateLatency) + { + int latency = _randomGenerator.Next(SimulationMinLatency, SimulationMaxLatency); + if (latency > MinLatencyThreshold) + { + lock (_pingSimulationList) + { + _pingSimulationList.Add(new IncomingData + { + Data = packet, + EndPoint = remoteEndPoint, + TimeWhenGet = DateTime.UtcNow.AddMilliseconds(latency) + }); + } + //hold packet + return; + } + } + + //ProcessEvents + DebugMessageReceived(packet, remoteEndPoint); + } + + private void DebugMessageReceived(NetPacket packet, IPEndPoint remoteEndPoint) + { +#endif + if (EnableStatistics) + { + Statistics.IncrementPacketsReceived(); + Statistics.AddBytesReceived(packet.Size); + } + + if (_ntpRequests.Count > 0) + { + if (_ntpRequests.TryGetValue(remoteEndPoint, out var request)) + { + if (packet.Size < 48) + { + NetDebug.Write(NetLogLevel.Trace, "NTP response too short: {}", packet.Size); + return; + } + + byte[] copiedData = new byte[packet.Size]; + Buffer.BlockCopy(packet.RawData, 0, copiedData, 0, packet.Size); + NtpPacket ntpPacket = NtpPacket.FromServerResponse(copiedData, DateTime.UtcNow); + try + { + ntpPacket.ValidateReply(); + } + catch (InvalidOperationException ex) + { + NetDebug.Write(NetLogLevel.Trace, "NTP response error: {}", ex.Message); + ntpPacket = null; + } + + if (ntpPacket != null) + { + _ntpRequests.Remove(remoteEndPoint); + _ntpEventListener?.OnNtpResponse(ntpPacket); + } + return; + } + } + + if (_extraPacketLayer != null) + { + int start = 0; + _extraPacketLayer.ProcessInboundPacket(ref remoteEndPoint, ref packet.RawData, ref start, ref packet.Size); + if (packet.Size == 0) + return; + } + + if (!packet.Verify()) + { + NetDebug.WriteError("[NM] DataReceived: bad!"); + PoolRecycle(packet); + return; + } + + switch (packet.Property) + { + //special case connect request + case PacketProperty.ConnectRequest: + if (NetConnectRequestPacket.GetProtocolId(packet) != NetConstants.ProtocolId) + { + SendRawAndRecycle(PoolGetWithProperty(PacketProperty.InvalidProtocol), remoteEndPoint); + return; + } + break; + //unconnected messages + case PacketProperty.Broadcast: + if (!BroadcastReceiveEnabled) + return; + CreateEvent(NetEvent.EType.Broadcast, remoteEndPoint: remoteEndPoint, readerSource: packet); + return; + case PacketProperty.UnconnectedMessage: + if (!UnconnectedMessagesEnabled) + return; + CreateEvent(NetEvent.EType.ReceiveUnconnected, remoteEndPoint: remoteEndPoint, readerSource: packet); + return; + case PacketProperty.NatMessage: + if (NatPunchEnabled) + NatPunchModule.ProcessMessage(remoteEndPoint, packet); + return; + } + + //Check normal packets + _peersLock.EnterReadLock(); + bool peerFound = _peersDict.TryGetValue(remoteEndPoint, out var netPeer); + _peersLock.ExitReadLock(); + + switch (packet.Property) + { + case PacketProperty.ConnectRequest: + var connRequest = NetConnectRequestPacket.FromData(packet); + if (connRequest != null) + ProcessConnectRequest(remoteEndPoint, netPeer, connRequest); + break; + case PacketProperty.PeerNotFound: + if (peerFound) //local + { + if (netPeer.ConnectionState != ConnectionState.Connected) + return; + if (packet.Size == 1) + { + //first reply + //send NetworkChanged packet + netPeer.ResetMtu(); + SendRaw(NetConnectAcceptPacket.MakeNetworkChanged(netPeer), remoteEndPoint); + NetDebug.Write($"PeerNotFound sending connection info: {remoteEndPoint}"); + } + else if (packet.Size == 2 && packet.RawData[1] == 1) + { + //second reply + DisconnectPeerForce(netPeer, DisconnectReason.PeerNotFound, 0, null); + } + } + else if (packet.Size > 1) //remote + { + //check if this is old peer + bool isOldPeer = false; + + if (AllowPeerAddressChange) + { + NetDebug.Write($"[NM] Looks like address change: {packet.Size}"); + var remoteData = NetConnectAcceptPacket.FromData(packet); + if (remoteData != null && + remoteData.PeerNetworkChanged && + remoteData.PeerId < _peersArray.Length) + { + _peersLock.EnterUpgradeableReadLock(); + var peer = _peersArray[remoteData.PeerId]; + if (peer != null && + peer.ConnectTime == remoteData.ConnectionTime && + peer.ConnectionNum == remoteData.ConnectionNumber) + { + if (peer.ConnectionState == ConnectionState.Connected) + { + peer.InitiateEndPointChange(); + if (_peerAddressChangedListener != null) + { + CreateEvent(NetEvent.EType.PeerAddressChanged, peer, remoteEndPoint); + } + NetDebug.Write("[NM] PeerNotFound change address of remote peer"); + } + isOldPeer = true; + } + _peersLock.ExitUpgradeableReadLock(); + } + } + + PoolRecycle(packet); + + //else peer really not found + if (!isOldPeer) + { + var secondResponse = PoolGetWithProperty(PacketProperty.PeerNotFound, 1); + secondResponse.RawData[1] = 1; + SendRawAndRecycle(secondResponse, remoteEndPoint); + } + } + break; + case PacketProperty.InvalidProtocol: + if (peerFound && netPeer.ConnectionState == ConnectionState.Outgoing) + DisconnectPeerForce(netPeer, DisconnectReason.InvalidProtocol, 0, null); + break; + case PacketProperty.Disconnect: + if (peerFound) + { + var disconnectResult = netPeer.ProcessDisconnect(packet); + if (disconnectResult == DisconnectResult.None) + { + PoolRecycle(packet); + return; + } + DisconnectPeerForce( + netPeer, + disconnectResult == DisconnectResult.Disconnect + ? DisconnectReason.RemoteConnectionClose + : DisconnectReason.ConnectionRejected, + 0, packet); + } + else + { + PoolRecycle(packet); + } + //Send shutdown + SendRawAndRecycle(PoolGetWithProperty(PacketProperty.ShutdownOk), remoteEndPoint); + break; + case PacketProperty.ConnectAccept: + if (!peerFound) + return; + var connAccept = NetConnectAcceptPacket.FromData(packet); + if (connAccept != null && netPeer.ProcessConnectAccept(connAccept)) + CreateEvent(NetEvent.EType.Connect, netPeer); + break; + default: + if(peerFound) + netPeer.ProcessPacket(packet); + else + SendRawAndRecycle(PoolGetWithProperty(PacketProperty.PeerNotFound), remoteEndPoint); + break; + } + } + + internal void CreateReceiveEvent(NetPacket packet, DeliveryMethod method, byte channelNumber, int headerSize, NetPeer fromPeer) + { + NetEvent evt; + lock (_eventLock) + { + evt = _netEventPoolHead; + if (evt == null) + evt = new NetEvent(this); + else + _netEventPoolHead = evt.Next; + } + evt.Type = NetEvent.EType.Receive; + evt.DataReader.SetSource(packet, headerSize); + evt.Peer = fromPeer; + evt.DeliveryMethod = method; + evt.ChannelNumber = channelNumber; + if (UnsyncedEvents || UnsyncedReceiveEvent || _manualMode) + { + ProcessEvent(evt); + } + else + { + lock(_netEventsProduceQueue) + _netEventsProduceQueue.Enqueue(evt); + } + } + + /// + /// Send data to all connected peers (channel - 0) + /// + /// DataWriter with data + /// Send options (reliable, unreliable, etc.) + public void SendToAll(NetDataWriter writer, DeliveryMethod options) + { + SendToAll(writer.Data, 0, writer.Length, options); + } + + /// + /// Send data to all connected peers (channel - 0) + /// + /// Data + /// Send options (reliable, unreliable, etc.) + public void SendToAll(byte[] data, DeliveryMethod options) + { + SendToAll(data, 0, data.Length, options); + } + + /// + /// Send data to all connected peers (channel - 0) + /// + /// Data + /// Start of data + /// Length of data + /// Send options (reliable, unreliable, etc.) + public void SendToAll(byte[] data, int start, int length, DeliveryMethod options) + { + SendToAll(data, start, length, 0, options); + } + + /// + /// Send data to all connected peers + /// + /// DataWriter with data + /// Number of channel (from 0 to channelsCount - 1) + /// Send options (reliable, unreliable, etc.) + public void SendToAll(NetDataWriter writer, byte channelNumber, DeliveryMethod options) + { + SendToAll(writer.Data, 0, writer.Length, channelNumber, options); + } + + /// + /// Send data to all connected peers + /// + /// Data + /// Number of channel (from 0 to channelsCount - 1) + /// Send options (reliable, unreliable, etc.) + public void SendToAll(byte[] data, byte channelNumber, DeliveryMethod options) + { + SendToAll(data, 0, data.Length, channelNumber, options); + } + + /// + /// Send data to all connected peers + /// + /// Data + /// Start of data + /// Length of data + /// Number of channel (from 0 to channelsCount - 1) + /// Send options (reliable, unreliable, etc.) + public void SendToAll(byte[] data, int start, int length, byte channelNumber, DeliveryMethod options) + { + try + { + _peersLock.EnterReadLock(); + for (var netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) + netPeer.Send(data, start, length, channelNumber, options); + } + finally + { + _peersLock.ExitReadLock(); + } + } + + /// + /// Send data to all connected peers (channel - 0) + /// + /// DataWriter with data + /// Send options (reliable, unreliable, etc.) + /// Excluded peer + public void SendToAll(NetDataWriter writer, DeliveryMethod options, NetPeer excludePeer) + { + SendToAll(writer.Data, 0, writer.Length, 0, options, excludePeer); + } + + /// + /// Send data to all connected peers (channel - 0) + /// + /// Data + /// Send options (reliable, unreliable, etc.) + /// Excluded peer + public void SendToAll(byte[] data, DeliveryMethod options, NetPeer excludePeer) + { + SendToAll(data, 0, data.Length, 0, options, excludePeer); + } + + /// + /// Send data to all connected peers (channel - 0) + /// + /// Data + /// Start of data + /// Length of data + /// Send options (reliable, unreliable, etc.) + /// Excluded peer + public void SendToAll(byte[] data, int start, int length, DeliveryMethod options, NetPeer excludePeer) + { + SendToAll(data, start, length, 0, options, excludePeer); + } + + /// + /// Send data to all connected peers + /// + /// DataWriter with data + /// Number of channel (from 0 to channelsCount - 1) + /// Send options (reliable, unreliable, etc.) + /// Excluded peer + public void SendToAll(NetDataWriter writer, byte channelNumber, DeliveryMethod options, NetPeer excludePeer) + { + SendToAll(writer.Data, 0, writer.Length, channelNumber, options, excludePeer); + } + + /// + /// Send data to all connected peers + /// + /// Data + /// Number of channel (from 0 to channelsCount - 1) + /// Send options (reliable, unreliable, etc.) + /// Excluded peer + public void SendToAll(byte[] data, byte channelNumber, DeliveryMethod options, NetPeer excludePeer) + { + SendToAll(data, 0, data.Length, channelNumber, options, excludePeer); + } + + + /// + /// Send data to all connected peers + /// + /// Data + /// Start of data + /// Length of data + /// Number of channel (from 0 to channelsCount - 1) + /// Send options (reliable, unreliable, etc.) + /// Excluded peer + public void SendToAll(byte[] data, int start, int length, byte channelNumber, DeliveryMethod options, NetPeer excludePeer) + { + try + { + _peersLock.EnterReadLock(); + for (var netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) + { + if (netPeer != excludePeer) + netPeer.Send(data, start, length, channelNumber, options); + } + } + finally + { + _peersLock.ExitReadLock(); + } + } + + /// + /// Start logic thread and listening on available port + /// + public bool Start() + { + return Start(0); + } + + /// + /// Start logic thread and listening on selected port + /// + /// bind to specific ipv4 address + /// bind to specific ipv6 address + /// port to listen + public bool Start(IPAddress addressIPv4, IPAddress addressIPv6, int port) + { + return Start(addressIPv4, addressIPv6, port, false); + } + + /// + /// Start logic thread and listening on selected port + /// + /// bind to specific ipv4 address + /// bind to specific ipv6 address + /// port to listen + public bool Start(string addressIPv4, string addressIPv6, int port) + { + IPAddress ipv4 = NetUtils.ResolveAddress(addressIPv4); + IPAddress ipv6 = NetUtils.ResolveAddress(addressIPv6); + return Start(ipv4, ipv6, port); + } + + /// + /// Start logic thread and listening on selected port + /// + /// port to listen + public bool Start(int port) + { + return Start(IPAddress.Any, IPAddress.IPv6Any, port); + } + + /// + /// Start in manual mode and listening on selected port + /// In this mode you should use ManualReceive (without PollEvents) for receive packets + /// and ManualUpdate(...) for update and send packets + /// This mode useful mostly for single-threaded servers + /// + /// bind to specific ipv4 address + /// bind to specific ipv6 address + /// port to listen + public bool StartInManualMode(IPAddress addressIPv4, IPAddress addressIPv6, int port) + { + return Start(addressIPv4, addressIPv6, port, true); + } + + /// + /// Start in manual mode and listening on selected port + /// In this mode you should use ManualReceive (without PollEvents) for receive packets + /// and ManualUpdate(...) for update and send packets + /// This mode useful mostly for single-threaded servers + /// + /// bind to specific ipv4 address + /// bind to specific ipv6 address + /// port to listen + public bool StartInManualMode(string addressIPv4, string addressIPv6, int port) + { + IPAddress ipv4 = NetUtils.ResolveAddress(addressIPv4); + IPAddress ipv6 = NetUtils.ResolveAddress(addressIPv6); + return StartInManualMode(ipv4, ipv6, port); + } + + /// + /// Start in manual mode and listening on selected port + /// In this mode you should use ManualReceive (without PollEvents) for receive packets + /// and ManualUpdate(...) for update and send packets + /// This mode useful mostly for single-threaded servers + /// + /// port to listen + public bool StartInManualMode(int port) + { + return StartInManualMode(IPAddress.Any, IPAddress.IPv6Any, port); + } + + /// + /// Send message without connection + /// + /// Raw data + /// Packet destination + /// Operation result + public bool SendUnconnectedMessage(byte[] message, IPEndPoint remoteEndPoint) + { + return SendUnconnectedMessage(message, 0, message.Length, remoteEndPoint); + } + + /// + /// Send message without connection. WARNING This method allocates a new IPEndPoint object and + /// synchronously makes a DNS request. If you're calling this method every frame it will be + /// much faster to just cache the IPEndPoint. + /// + /// Data serializer + /// Packet destination IP or hostname + /// Packet destination port + /// Operation result + public bool SendUnconnectedMessage(NetDataWriter writer, string address, int port) + { + IPEndPoint remoteEndPoint = NetUtils.MakeEndPoint(address, port); + + return SendUnconnectedMessage(writer.Data, 0, writer.Length, remoteEndPoint); + } + + /// + /// Send message without connection + /// + /// Data serializer + /// Packet destination + /// Operation result + public bool SendUnconnectedMessage(NetDataWriter writer, IPEndPoint remoteEndPoint) + { + return SendUnconnectedMessage(writer.Data, 0, writer.Length, remoteEndPoint); + } + + /// + /// Send message without connection + /// + /// Raw data + /// data start + /// data length + /// Packet destination + /// Operation result + public bool SendUnconnectedMessage(byte[] message, int start, int length, IPEndPoint remoteEndPoint) + { + //No need for CRC here, SendRaw does that + NetPacket packet = PoolGetWithData(PacketProperty.UnconnectedMessage, message, start, length); + return SendRawAndRecycle(packet, remoteEndPoint) > 0; + } + + /// + /// Triggers update and send logic immediately (works asynchronously) + /// + public void TriggerUpdate() + { + _updateTriggerEvent.Set(); + } + + /// + /// Receive all pending events. Call this in game update code + /// In Manual mode it will call also socket Receive (which can be slow) + /// + public void PollEvents() + { + if (_manualMode) + { + if (_udpSocketv4 != null) + ManualReceive(_udpSocketv4, _bufferEndPointv4); + if (_udpSocketv6 != null && _udpSocketv6 != _udpSocketv4) + ManualReceive(_udpSocketv6, _bufferEndPointv6); + ProcessDelayedPackets(); + return; + } + if (UnsyncedEvents) + return; + lock (_netEventsProduceQueue) + { + (_netEventsConsumeQueue, _netEventsProduceQueue) = (_netEventsProduceQueue, _netEventsConsumeQueue); + } + + while(_netEventsConsumeQueue.Count > 0) + ProcessEvent(_netEventsConsumeQueue.Dequeue()); + } + + /// + /// Connect to remote host + /// + /// Server IP or hostname + /// Server Port + /// Connection key + /// New NetPeer if new connection, Old NetPeer if already connected, null peer if there is ConnectionRequest awaiting + /// Manager is not running. Call + public NetPeer Connect(string address, int port, string key) + { + return Connect(address, port, NetDataWriter.FromString(key)); + } + + /// + /// Connect to remote host + /// + /// Server IP or hostname + /// Server Port + /// Additional data for remote peer + /// New NetPeer if new connection, Old NetPeer if already connected, null peer if there is ConnectionRequest awaiting + /// Manager is not running. Call + public NetPeer Connect(string address, int port, NetDataWriter connectionData) + { + IPEndPoint ep; + try + { + ep = NetUtils.MakeEndPoint(address, port); + } + catch + { + CreateEvent(NetEvent.EType.Disconnect, disconnectReason: DisconnectReason.UnknownHost); + return null; + } + return Connect(ep, connectionData); + } + + /// + /// Connect to remote host + /// + /// Server end point (ip and port) + /// Connection key + /// New NetPeer if new connection, Old NetPeer if already connected, null peer if there is ConnectionRequest awaiting + /// Manager is not running. Call + public NetPeer Connect(IPEndPoint target, string key) + { + return Connect(target, NetDataWriter.FromString(key)); + } + + public bool IsClient { get; private set; } + /// + /// Connect to remote host + /// + /// Server end point (ip and port) + /// Additional data for remote peer + /// New NetPeer if new connection, Old NetPeer if already connected, null peer if there is ConnectionRequest awaiting + /// Manager is not running. Call + public NetPeer Connect(IPEndPoint target, NetDataWriter connectionData) + { + if (!IsRunning) + throw new InvalidOperationException("Client is not running"); + + lock(_requestsDict) + { + if (_requestsDict.ContainsKey(target)) + return null; + } + + byte connectionNumber = 0; + _peersLock.EnterUpgradeableReadLock(); + if (_peersDict.TryGetValue(target, out var peer)) + { + switch (peer.ConnectionState) + { + //just return already connected peer + case ConnectionState.Connected: + case ConnectionState.Outgoing: + _peersLock.ExitUpgradeableReadLock(); + return peer; + } + //else reconnect + connectionNumber = (byte)((peer.ConnectionNum + 1) % NetConstants.MaxConnectionNumber); + RemovePeer(peer); + } + + //Create reliable connection + //And send connection request + peer = new NetPeer(this, target, GetNextPeerId(), connectionNumber, connectionData); + AddPeer(peer); + _peersLock.ExitUpgradeableReadLock(); + IsClient = true; + + return peer; + } + + /// + /// Force closes connection and stop all threads. + /// + public void Stop() + { + Stop(true); + } + + /// + /// Force closes connection and stop all threads. + /// + /// Send disconnect messages + public void Stop(bool sendDisconnectMessages) + { + if (!IsRunning) + return; + NetDebug.Write("[NM] Stop"); + + IsClient = false; + //Send last disconnect + for(var netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) + netPeer.Shutdown(null, 0, 0, !sendDisconnectMessages); + + //Stop + CloseSocket(false); + _updateTriggerEvent.Set(); + if (!_manualMode) + { + _logicThread.Join(); + _logicThread = null; + } + + //clear peers + _peersLock.EnterWriteLock(); + _headPeer = null; + _peersDict.Clear(); + _peersArray = new NetPeer[32]; + _peersLock.ExitWriteLock(); + _peerIds = new ConcurrentQueue(); + _lastPeerId = 0; +#if DEBUG + lock (_pingSimulationList) + _pingSimulationList.Clear(); +#endif + _connectedPeersCount = 0; + _netEventsProduceQueue.Clear(); + _netEventsConsumeQueue.Clear(); + } + + /// + /// Return peers count with connection state + /// + /// peer connection state (you can use as bit flags) + /// peers count + public int GetPeersCount(ConnectionState peerState) + { + int count = 0; + _peersLock.EnterReadLock(); + for (var netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) + { + if ((netPeer.ConnectionState & peerState) != 0) + count++; + } + _peersLock.ExitReadLock(); + return count; + } + + /// + /// Get copy of peers (without allocations) + /// + /// List that will contain result + /// State of peers + public void GetPeersNonAlloc(List peers, ConnectionState peerState) + { + peers.Clear(); + _peersLock.EnterReadLock(); + for (var netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) + { + if ((netPeer.ConnectionState & peerState) != 0) + peers.Add(netPeer); + } + _peersLock.ExitReadLock(); + } + + /// + /// Disconnect all peers without any additional data + /// + public void DisconnectAll() + { + DisconnectAll(null, 0, 0); + } + + /// + /// Disconnect all peers with shutdown message + /// + /// Data to send (must be less or equal MTU) + /// Data start + /// Data count + public void DisconnectAll(byte[] data, int start, int count) + { + //Send disconnect packets + _peersLock.EnterReadLock(); + for (var netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) + { + DisconnectPeer( + netPeer, + DisconnectReason.DisconnectPeerCalled, + 0, + false, + data, + start, + count, + null); + } + _peersLock.ExitReadLock(); + } + + /// + /// Immediately disconnect peer from server without additional data + /// + /// peer to disconnect + public void DisconnectPeerForce(NetPeer peer) + { + DisconnectPeerForce(peer, DisconnectReason.DisconnectPeerCalled, 0, null); + } + + /// + /// Disconnect peer from server + /// + /// peer to disconnect + public void DisconnectPeer(NetPeer peer) + { + DisconnectPeer(peer, null, 0, 0); + } + + /// + /// Disconnect peer from server and send additional data (Size must be less or equal MTU - 8) + /// + /// peer to disconnect + /// additional data + public void DisconnectPeer(NetPeer peer, byte[] data) + { + DisconnectPeer(peer, data, 0, data.Length); + } + + /// + /// Disconnect peer from server and send additional data (Size must be less or equal MTU - 8) + /// + /// peer to disconnect + /// additional data + public void DisconnectPeer(NetPeer peer, NetDataWriter writer) + { + DisconnectPeer(peer, writer.Data, 0, writer.Length); + } + + /// + /// Disconnect peer from server and send additional data (Size must be less or equal MTU - 8) + /// + /// peer to disconnect + /// additional data + /// data start + /// data length + public void DisconnectPeer(NetPeer peer, byte[] data, int start, int count) + { + DisconnectPeer( + peer, + DisconnectReason.DisconnectPeerCalled, + 0, + false, + data, + start, + count, + null); + } + + /// + /// Create the requests for NTP server + /// + /// NTP Server address. + public void CreateNtpRequest(IPEndPoint endPoint) + { + _ntpRequests.Add(endPoint, new NtpRequest(endPoint)); + } + + /// + /// Create the requests for NTP server + /// + /// NTP Server address. + /// port + public void CreateNtpRequest(string ntpServerAddress, int port) + { + IPEndPoint endPoint = NetUtils.MakeEndPoint(ntpServerAddress, port); + _ntpRequests.Add(endPoint, new NtpRequest(endPoint)); + } + + /// + /// Create the requests for NTP server (default port) + /// + /// NTP Server address. + public void CreateNtpRequest(string ntpServerAddress) + { + IPEndPoint endPoint = NetUtils.MakeEndPoint(ntpServerAddress, NtpRequest.DefaultPort); + _ntpRequests.Add(endPoint, new NtpRequest(endPoint)); + } + + public NetPeerEnumerator GetEnumerator() + { + return new NetPeerEnumerator(_headPeer); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new NetPeerEnumerator(_headPeer); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new NetPeerEnumerator(_headPeer); + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.cs.meta new file mode 100644 index 0000000..673e75f --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 37d95580df7122c44b9333cd3ab77732 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPacket.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPacket.cs new file mode 100644 index 0000000..7e29fe9 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPacket.cs @@ -0,0 +1,161 @@ +using System; +using System.Net; +using LiteNetLib.Utils; + +namespace LiteNetLib +{ + internal enum PacketProperty : byte + { + Unreliable, + Channeled, + Ack, + Ping, + Pong, + ConnectRequest, + ConnectAccept, + Disconnect, + UnconnectedMessage, + MtuCheck, + MtuOk, + Broadcast, + Merged, + ShutdownOk, + PeerNotFound, + InvalidProtocol, + NatMessage, + Empty + } + + internal sealed class NetPacket + { + private static readonly int PropertiesCount = Enum.GetValues(typeof(PacketProperty)).Length; + private static readonly int[] HeaderSizes; + + static NetPacket() + { + HeaderSizes = NetUtils.AllocatePinnedUninitializedArray(PropertiesCount); + for (int i = 0; i < HeaderSizes.Length; i++) + { + switch ((PacketProperty)i) + { + case PacketProperty.Channeled: + case PacketProperty.Ack: + HeaderSizes[i] = NetConstants.ChanneledHeaderSize; + break; + case PacketProperty.Ping: + HeaderSizes[i] = NetConstants.HeaderSize + 2; + break; + case PacketProperty.ConnectRequest: + HeaderSizes[i] = NetConnectRequestPacket.HeaderSize; + break; + case PacketProperty.ConnectAccept: + HeaderSizes[i] = NetConnectAcceptPacket.Size; + break; + case PacketProperty.Disconnect: + HeaderSizes[i] = NetConstants.HeaderSize + 8; + break; + case PacketProperty.Pong: + HeaderSizes[i] = NetConstants.HeaderSize + 10; + break; + default: + HeaderSizes[i] = NetConstants.HeaderSize; + break; + } + } + } + + //Header + public PacketProperty Property + { + get => (PacketProperty)(RawData[0] & 0x1F); + set => RawData[0] = (byte)((RawData[0] & 0xE0) | (byte)value); + } + + public byte ConnectionNumber + { + get => (byte)((RawData[0] & 0x60) >> 5); + set => RawData[0] = (byte) ((RawData[0] & 0x9F) | (value << 5)); + } + + public ushort Sequence + { + get => BitConverter.ToUInt16(RawData, 1); + set => FastBitConverter.GetBytes(RawData, 1, value); + } + + public bool IsFragmented => (RawData[0] & 0x80) != 0; + + public void MarkFragmented() + { + RawData[0] |= 0x80; //set first bit + } + + public byte ChannelId + { + get => RawData[3]; + set => RawData[3] = value; + } + + public ushort FragmentId + { + get => BitConverter.ToUInt16(RawData, 4); + set => FastBitConverter.GetBytes(RawData, 4, value); + } + + public ushort FragmentPart + { + get => BitConverter.ToUInt16(RawData, 6); + set => FastBitConverter.GetBytes(RawData, 6, value); + } + + public ushort FragmentsTotal + { + get => BitConverter.ToUInt16(RawData, 8); + set => FastBitConverter.GetBytes(RawData, 8, value); + } + + //Data + public byte[] RawData; + public int Size; + + //Delivery + public object UserData; + + //Pool node + public NetPacket Next; + + public NetPacket(int size) + { + RawData = new byte[size]; + Size = size; + } + + public NetPacket(PacketProperty property, int size) + { + size += GetHeaderSize(property); + RawData = new byte[size]; + Property = property; + Size = size; + } + + public static int GetHeaderSize(PacketProperty property) + { + return HeaderSizes[(int)property]; + } + + public int GetHeaderSize() + { + return HeaderSizes[RawData[0] & 0x1F]; + } + + public bool Verify() + { + byte property = (byte)(RawData[0] & 0x1F); + if (property >= PropertiesCount) + return false; + int headerSize = HeaderSizes[property]; + bool fragmented = (RawData[0] & 0x80) != 0; + return Size >= headerSize && (!fragmented || Size >= headerSize + NetConstants.FragmentHeaderSize); + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPacket.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPacket.cs.meta new file mode 100644 index 0000000..02838af --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPacket.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e7ddd322169be074f870f73db1a55255 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPeer.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPeer.cs new file mode 100644 index 0000000..0ebf508 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPeer.cs @@ -0,0 +1,1405 @@ +#if DEBUG +#define STATS_ENABLED +#endif +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Net; +using System.Threading; +using LiteNetLib.Utils; + +namespace LiteNetLib +{ + /// + /// Peer connection state + /// + [Flags] + public enum ConnectionState : byte + { + Outgoing = 1 << 1, + Connected = 1 << 2, + ShutdownRequested = 1 << 3, + Disconnected = 1 << 4, + EndPointChange = 1 << 5, + Any = Outgoing | Connected | ShutdownRequested | EndPointChange + } + + internal enum ConnectRequestResult + { + None, + P2PLose, //when peer connecting + Reconnection, //when peer was connected + NewConnection //when peer was disconnected + } + + internal enum DisconnectResult + { + None, + Reject, + Disconnect + } + + internal enum ShutdownResult + { + None, + Success, + WasConnected + } + + /// + /// Network peer. Main purpose is sending messages to specific peer. + /// + public class NetPeer + { + //Ping and RTT + private int _rtt; + private int _avgRtt; + private int _rttCount; + private double _resendDelay = 27.0; + private int _pingSendTimer; + private int _rttResetTimer; + private readonly Stopwatch _pingTimer = new Stopwatch(); + private int _timeSinceLastPacket; + private long _remoteDelta; + + //Common + private readonly object _shutdownLock = new object(); + + internal volatile NetPeer NextPeer; + internal NetPeer PrevPeer; + + internal byte ConnectionNum + { + get => _connectNum; + private set + { + _connectNum = value; + _mergeData.ConnectionNumber = value; + _pingPacket.ConnectionNumber = value; + _pongPacket.ConnectionNumber = value; + } + } + + //Channels + private readonly Queue _unreliableChannel; + private readonly ConcurrentQueue _channelSendQueue; + private readonly BaseChannel[] _channels; + + //MTU + private int _mtu; + private int _mtuIdx; + private bool _finishMtu; + private int _mtuCheckTimer; + private int _mtuCheckAttempts; + private const int MtuCheckDelay = 1000; + private const int MaxMtuCheckAttempts = 4; + private readonly object _mtuMutex = new object(); + + //Fragment + private class IncomingFragments + { + public NetPacket[] Fragments; + public int ReceivedCount; + public int TotalSize; + public byte ChannelId; + } + private int _fragmentId; + private readonly Dictionary _holdedFragments; + private readonly Dictionary _deliveredFragments; + + //Merging + private readonly NetPacket _mergeData; + private int _mergePos; + private int _mergeCount; + + //Connection + private IPEndPoint _remoteEndPoint; + private int _connectAttempts; + private int _connectTimer; + private long _connectTime; + private byte _connectNum; + private ConnectionState _connectionState; + private NetPacket _shutdownPacket; + private const int ShutdownDelay = 300; + private int _shutdownTimer; + private readonly NetPacket _pingPacket; + private readonly NetPacket _pongPacket; + private readonly NetPacket _connectRequestPacket; + private readonly NetPacket _connectAcceptPacket; + + /// + /// Peer ip address and port + /// + public IPEndPoint EndPoint => _remoteEndPoint; + + /// + /// Peer parent NetManager + /// + public readonly NetManager NetManager; + + /// + /// Current connection state + /// + public ConnectionState ConnectionState => _connectionState; + + /// + /// Connection time for internal purposes + /// + internal long ConnectTime => _connectTime; + + /// + /// Peer id can be used as key in your dictionary of peers + /// + public readonly int Id; + + /// + /// Id assigned from server + /// + public int RemoteId { get; private set; } + + /// + /// Current one-way ping (RTT/2) in milliseconds + /// + public int Ping => _avgRtt/2; + + /// + /// Round trip time in milliseconds + /// + public int RoundTripTime => _avgRtt; + + /// + /// Current MTU - Maximum Transfer Unit ( maximum udp packet size without fragmentation ) + /// + public int Mtu => _mtu; + + /// + /// Delta with remote time in ticks (not accurate) + /// positive - remote time > our time + /// + public long RemoteTimeDelta => _remoteDelta; + + /// + /// Remote UTC time (not accurate) + /// + public DateTime RemoteUtcTime => new DateTime(DateTime.UtcNow.Ticks + _remoteDelta); + + /// + /// Time since last packet received (including internal library packets) + /// + public int TimeSinceLastPacket => _timeSinceLastPacket; + + internal double ResendDelay => _resendDelay; + + /// + /// Application defined object containing data about the connection + /// + public object Tag; + + /// + /// Statistics of peer connection + /// + public readonly NetStatistics Statistics; + + //incoming connection constructor + internal NetPeer(NetManager netManager, IPEndPoint remoteEndPoint, int id) + { + Id = id; + Statistics = new NetStatistics(); + NetManager = netManager; + ResetMtu(); + _remoteEndPoint = remoteEndPoint; + _connectionState = ConnectionState.Connected; + _mergeData = new NetPacket(PacketProperty.Merged, NetConstants.MaxPacketSize); + _pongPacket = new NetPacket(PacketProperty.Pong, 0); + _pingPacket = new NetPacket(PacketProperty.Ping, 0) {Sequence = 1}; + + _unreliableChannel = new Queue(); + _holdedFragments = new Dictionary(); + _deliveredFragments = new Dictionary(); + + _channels = new BaseChannel[netManager.ChannelsCount * NetConstants.ChannelTypeCount]; + _channelSendQueue = new ConcurrentQueue(); + } + + internal void InitiateEndPointChange() + { + ResetMtu(); + _connectionState = ConnectionState.EndPointChange; + } + + internal void FinishEndPointChange(IPEndPoint newEndPoint) + { + if (_connectionState != ConnectionState.EndPointChange) + return; + _connectionState = ConnectionState.Connected; + _remoteEndPoint = newEndPoint; + } + + internal void ResetMtu() + { + _finishMtu = false; + if (NetManager.MtuOverride > 0) + OverrideMtu(NetManager.MtuOverride); + else if (NetManager.UseSafeMtu) + SetMtu(0); + else + SetMtu(1); + } + + private void SetMtu(int mtuIdx) + { + _mtuIdx = mtuIdx; + _mtu = NetConstants.PossibleMtu[mtuIdx] - NetManager.ExtraPacketSizeForLayer; + } + + private void OverrideMtu(int mtuValue) + { + _mtu = mtuValue; + _finishMtu = true; + } + + /// + /// Returns packets count in queue for reliable channel + /// + /// number of channel 0-63 + /// type of channel ReliableOrdered or ReliableUnordered + /// packets count in channel queue + public int GetPacketsCountInReliableQueue(byte channelNumber, bool ordered) + { + int idx = channelNumber * NetConstants.ChannelTypeCount + + (byte) (ordered ? DeliveryMethod.ReliableOrdered : DeliveryMethod.ReliableUnordered); + var channel = _channels[idx]; + return channel != null ? ((ReliableChannel)channel).PacketsInQueue : 0; + } + + /// + /// Create temporary packet (maximum size MTU - headerSize) to send later without additional copies + /// + /// Delivery method (reliable, unreliable, etc.) + /// Number of channel (from 0 to channelsCount - 1) + /// PooledPacket that you can use to write data starting from UserDataOffset + public PooledPacket CreatePacketFromPool(DeliveryMethod deliveryMethod, byte channelNumber) + { + //multithreaded variable + int mtu = _mtu; + var packet = NetManager.PoolGetPacket(mtu); + if (deliveryMethod == DeliveryMethod.Unreliable) + { + packet.Property = PacketProperty.Unreliable; + return new PooledPacket(packet, mtu, 0); + } + else + { + packet.Property = PacketProperty.Channeled; + return new PooledPacket(packet, mtu, (byte)(channelNumber * NetConstants.ChannelTypeCount + (byte)deliveryMethod)); + } + } + + /// + /// Sends pooled packet without data copy + /// + /// packet to send + /// size of user data you want to send + public void SendPooledPacket(PooledPacket packet, int userDataSize) + { + if (_connectionState != ConnectionState.Connected) + return; + packet._packet.Size = packet.UserDataOffset + userDataSize; + if (packet._packet.Property == PacketProperty.Channeled) + { + CreateChannel(packet._channelNumber).AddToQueue(packet._packet); + } + else + { + lock(_unreliableChannel) + _unreliableChannel.Enqueue(packet._packet); + } + } + + private BaseChannel CreateChannel(byte idx) + { + BaseChannel newChannel = _channels[idx]; + if (newChannel != null) + return newChannel; + switch ((DeliveryMethod)(idx % NetConstants.ChannelTypeCount)) + { + case DeliveryMethod.ReliableUnordered: + newChannel = new ReliableChannel(this, false, idx); + break; + case DeliveryMethod.Sequenced: + newChannel = new SequencedChannel(this, false, idx); + break; + case DeliveryMethod.ReliableOrdered: + newChannel = new ReliableChannel(this, true, idx); + break; + case DeliveryMethod.ReliableSequenced: + newChannel = new SequencedChannel(this, true, idx); + break; + } + BaseChannel prevChannel = Interlocked.CompareExchange(ref _channels[idx], newChannel, null); + if (prevChannel != null) + return prevChannel; + + return newChannel; + } + + //"Connect to" constructor + internal NetPeer(NetManager netManager, IPEndPoint remoteEndPoint, int id, byte connectNum, NetDataWriter connectData) + : this(netManager, remoteEndPoint, id) + { + _connectTime = DateTime.UtcNow.Ticks; + _connectionState = ConnectionState.Outgoing; + ConnectionNum = connectNum; + + //Make initial packet + _connectRequestPacket = NetConnectRequestPacket.Make(connectData, remoteEndPoint.Serialize(), _connectTime, id); + _connectRequestPacket.ConnectionNumber = connectNum; + + //Send request + NetManager.SendRaw(_connectRequestPacket, _remoteEndPoint); + + NetDebug.Write(NetLogLevel.Trace, $"[CC] ConnectId: {_connectTime}, ConnectNum: {connectNum}"); + } + + //"Accept" incoming constructor + internal NetPeer(NetManager netManager, ConnectionRequest request, int id) + : this(netManager, request.RemoteEndPoint, id) + { + _connectTime = request.InternalPacket.ConnectionTime; + ConnectionNum = request.InternalPacket.ConnectionNumber; + RemoteId = request.InternalPacket.PeerId; + + //Make initial packet + _connectAcceptPacket = NetConnectAcceptPacket.Make(_connectTime, ConnectionNum, id); + + //Make Connected + _connectionState = ConnectionState.Connected; + + //Send + NetManager.SendRaw(_connectAcceptPacket, _remoteEndPoint); + + NetDebug.Write(NetLogLevel.Trace, $"[CC] ConnectId: {_connectTime}"); + } + + //Reject + internal void Reject(NetConnectRequestPacket requestData, byte[] data, int start, int length) + { + _connectTime = requestData.ConnectionTime; + _connectNum = requestData.ConnectionNumber; + Shutdown(data, start, length, false); + } + + internal bool ProcessConnectAccept(NetConnectAcceptPacket packet) + { + if (_connectionState != ConnectionState.Outgoing) + return false; + + //check connection id + if (packet.ConnectionTime != _connectTime) + { + NetDebug.Write(NetLogLevel.Trace, $"[NC] Invalid connectId: {packet.ConnectionTime} != our({_connectTime})"); + return false; + } + //check connect num + ConnectionNum = packet.ConnectionNumber; + RemoteId = packet.PeerId; + + NetDebug.Write(NetLogLevel.Trace, "[NC] Received connection accept"); + Interlocked.Exchange(ref _timeSinceLastPacket, 0); + _connectionState = ConnectionState.Connected; + return true; + } + + /// + /// Gets maximum size of packet that will be not fragmented. + /// + /// Type of packet that you want send + /// size in bytes + public int GetMaxSinglePacketSize(DeliveryMethod options) + { + return _mtu - NetPacket.GetHeaderSize(options == DeliveryMethod.Unreliable ? PacketProperty.Unreliable : PacketProperty.Channeled); + } + + /// + /// Send data to peer with delivery event called + /// + /// Data + /// Number of channel (from 0 to channelsCount - 1) + /// Delivery method (reliable, unreliable, etc.) + /// User data that will be received in DeliveryEvent + /// + /// If you trying to send unreliable packet type + /// + public void SendWithDeliveryEvent(byte[] data, byte channelNumber, DeliveryMethod deliveryMethod, object userData) + { + if (deliveryMethod != DeliveryMethod.ReliableOrdered && deliveryMethod != DeliveryMethod.ReliableUnordered) + throw new ArgumentException("Delivery event will work only for ReliableOrdered/Unordered packets"); + Send_Internal(data, 0, data.Length, channelNumber, deliveryMethod, userData); + } + + /// + /// Send data to peer with delivery event called + /// + /// Data + /// Start of data + /// Length of data + /// Number of channel (from 0 to channelsCount - 1) + /// Delivery method (reliable, unreliable, etc.) + /// User data that will be received in DeliveryEvent + /// + /// If you trying to send unreliable packet type + /// + public void SendWithDeliveryEvent(byte[] data, int start, int length, byte channelNumber, DeliveryMethod deliveryMethod, object userData) + { + if (deliveryMethod != DeliveryMethod.ReliableOrdered && deliveryMethod != DeliveryMethod.ReliableUnordered) + throw new ArgumentException("Delivery event will work only for ReliableOrdered/Unordered packets"); + Send_Internal(data, start, length, channelNumber, deliveryMethod, userData); + } + + /// + /// Send data to peer with delivery event called + /// + /// Data + /// Number of channel (from 0 to channelsCount - 1) + /// Delivery method (reliable, unreliable, etc.) + /// User data that will be received in DeliveryEvent + /// + /// If you trying to send unreliable packet type + /// + public void SendWithDeliveryEvent(NetDataWriter dataWriter, byte channelNumber, DeliveryMethod deliveryMethod, object userData) + { + if (deliveryMethod != DeliveryMethod.ReliableOrdered && deliveryMethod != DeliveryMethod.ReliableUnordered) + throw new ArgumentException("Delivery event will work only for ReliableOrdered/Unordered packets"); + Send_Internal(dataWriter.Data, 0, dataWriter.Length, channelNumber, deliveryMethod, userData); + } + + /// + /// Send data to peer (channel - 0) + /// + /// Data + /// Send options (reliable, unreliable, etc.) + /// + /// If size exceeds maximum limit: + /// MTU - headerSize bytes for Unreliable + /// Fragment count exceeded ushort.MaxValue + /// + public void Send(byte[] data, DeliveryMethod deliveryMethod) + { + Send_Internal(data, 0, data.Length, 0, deliveryMethod, null); + } + + /// + /// Send data to peer (channel - 0) + /// + /// DataWriter with data + /// Send options (reliable, unreliable, etc.) + /// + /// If size exceeds maximum limit: + /// MTU - headerSize bytes for Unreliable + /// Fragment count exceeded ushort.MaxValue + /// + public void Send(NetDataWriter dataWriter, DeliveryMethod deliveryMethod) + { + Send_Internal(dataWriter.Data, 0, dataWriter.Length, 0, deliveryMethod, null); + } + + /// + /// Send data to peer (channel - 0) + /// + /// Data + /// Start of data + /// Length of data + /// Send options (reliable, unreliable, etc.) + /// + /// If size exceeds maximum limit: + /// MTU - headerSize bytes for Unreliable + /// Fragment count exceeded ushort.MaxValue + /// + public void Send(byte[] data, int start, int length, DeliveryMethod options) + { + Send_Internal(data, start, length, 0, options, null); + } + + /// + /// Send data to peer + /// + /// Data + /// Number of channel (from 0 to channelsCount - 1) + /// Send options (reliable, unreliable, etc.) + /// + /// If size exceeds maximum limit: + /// MTU - headerSize bytes for Unreliable + /// Fragment count exceeded ushort.MaxValue + /// + public void Send(byte[] data, byte channelNumber, DeliveryMethod deliveryMethod) + { + Send_Internal(data, 0, data.Length, channelNumber, deliveryMethod, null); + } + + /// + /// Send data to peer + /// + /// DataWriter with data + /// Number of channel (from 0 to channelsCount - 1) + /// Send options (reliable, unreliable, etc.) + /// + /// If size exceeds maximum limit: + /// MTU - headerSize bytes for Unreliable + /// Fragment count exceeded ushort.MaxValue + /// + public void Send(NetDataWriter dataWriter, byte channelNumber, DeliveryMethod deliveryMethod) + { + Send_Internal(dataWriter.Data, 0, dataWriter.Length, channelNumber, deliveryMethod, null); + } + + /// + /// Send data to peer + /// + /// Data + /// Start of data + /// Length of data + /// Number of channel (from 0 to channelsCount - 1) + /// Delivery method (reliable, unreliable, etc.) + /// + /// If size exceeds maximum limit: + /// MTU - headerSize bytes for Unreliable + /// Fragment count exceeded ushort.MaxValue + /// + public void Send(byte[] data, int start, int length, byte channelNumber, DeliveryMethod deliveryMethod) + { + Send_Internal(data, start, length, channelNumber, deliveryMethod, null); + } + + private void Send_Internal( + byte[] data, + int start, + int length, + byte channelNumber, + DeliveryMethod deliveryMethod, + object userData) + { + if (_connectionState != ConnectionState.Connected || channelNumber >= _channels.Length) + return; + + //Select channel + PacketProperty property; + BaseChannel channel = null; + + if (deliveryMethod == DeliveryMethod.Unreliable) + { + property = PacketProperty.Unreliable; + } + else + { + property = PacketProperty.Channeled; + channel = CreateChannel((byte)(channelNumber * NetConstants.ChannelTypeCount + (byte)deliveryMethod)); + } + + //Prepare + NetDebug.Write("[RS]Packet: " + property); + + //Check fragmentation + int headerSize = NetPacket.GetHeaderSize(property); + //Save mtu for multithread + int mtu = _mtu; + if (length + headerSize > mtu) + { + //if cannot be fragmented + if (deliveryMethod != DeliveryMethod.ReliableOrdered && deliveryMethod != DeliveryMethod.ReliableUnordered) + throw new TooBigPacketException("Unreliable or ReliableSequenced packet size exceeded maximum of " + (mtu - headerSize) + " bytes, Check allowed size by GetMaxSinglePacketSize()"); + + int packetFullSize = mtu - headerSize; + int packetDataSize = packetFullSize - NetConstants.FragmentHeaderSize; + int totalPackets = length / packetDataSize + (length % packetDataSize == 0 ? 0 : 1); + + NetDebug.Write("FragmentSend:\n" + + " MTU: {0}\n" + + " headerSize: {1}\n" + + " packetFullSize: {2}\n" + + " packetDataSize: {3}\n" + + " totalPackets: {4}", + mtu, headerSize, packetFullSize, packetDataSize, totalPackets); + + if (totalPackets > ushort.MaxValue) + throw new TooBigPacketException("Data was split in " + totalPackets + " fragments, which exceeds " + ushort.MaxValue); + + ushort currentFragmentId = (ushort)Interlocked.Increment(ref _fragmentId); + + for(ushort partIdx = 0; partIdx < totalPackets; partIdx++) + { + int sendLength = length > packetDataSize ? packetDataSize : length; + + NetPacket p = NetManager.PoolGetPacket(headerSize + sendLength + NetConstants.FragmentHeaderSize); + p.Property = property; + p.UserData = userData; + p.FragmentId = currentFragmentId; + p.FragmentPart = partIdx; + p.FragmentsTotal = (ushort)totalPackets; + p.MarkFragmented(); + + Buffer.BlockCopy(data, start + partIdx * packetDataSize, p.RawData, NetConstants.FragmentedHeaderTotalSize, sendLength); + channel.AddToQueue(p); + + length -= sendLength; + } + return; + } + + //Else just send + NetPacket packet = NetManager.PoolGetPacket(headerSize + length); + packet.Property = property; + Buffer.BlockCopy(data, start, packet.RawData, headerSize, length); + packet.UserData = userData; + + if (channel == null) //unreliable + { + lock(_unreliableChannel) + _unreliableChannel.Enqueue(packet); + } + else + { + channel.AddToQueue(packet); + } + } + +#if LITENETLIB_SPANS || NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1 || NETCOREAPP3_1 || NET5_0 || NETSTANDARD2_1 + /// + /// Send data to peer with delivery event called + /// + /// Data + /// Number of channel (from 0 to channelsCount - 1) + /// Delivery method (reliable, unreliable, etc.) + /// User data that will be received in DeliveryEvent + /// + /// If you trying to send unreliable packet type + /// + public void SendWithDeliveryEvent(ReadOnlySpan data, byte channelNumber, DeliveryMethod deliveryMethod, object userData) + { + if (deliveryMethod != DeliveryMethod.ReliableOrdered && deliveryMethod != DeliveryMethod.ReliableUnordered) + throw new ArgumentException("Delivery event will work only for ReliableOrdered/Unordered packets"); + Send_Internal(data, channelNumber, deliveryMethod, userData); + } + + /// + /// Send data to peer (channel - 0) + /// + /// Data + /// Send options (reliable, unreliable, etc.) + /// + /// If size exceeds maximum limit: + /// MTU - headerSize bytes for Unreliable + /// Fragment count exceeded ushort.MaxValue + /// + public void Send(ReadOnlySpan data, DeliveryMethod deliveryMethod) + { + Send_Internal(data, 0, deliveryMethod, null); + } + + /// + /// Send data to peer + /// + /// Data + /// Number of channel (from 0 to channelsCount - 1) + /// Send options (reliable, unreliable, etc.) + /// + /// If size exceeds maximum limit: + /// MTU - headerSize bytes for Unreliable + /// Fragment count exceeded ushort.MaxValue + /// + public void Send(ReadOnlySpan data, byte channelNumber, DeliveryMethod deliveryMethod) + { + Send_Internal(data, channelNumber, deliveryMethod, null); + } + + private void Send_Internal( + ReadOnlySpan data, + byte channelNumber, + DeliveryMethod deliveryMethod, + object userData) + { + if (_connectionState != ConnectionState.Connected || channelNumber >= _channels.Length) + return; + + //Select channel + PacketProperty property; + BaseChannel channel = null; + + if (deliveryMethod == DeliveryMethod.Unreliable) + { + property = PacketProperty.Unreliable; + } + else + { + property = PacketProperty.Channeled; + channel = CreateChannel((byte)(channelNumber * NetConstants.ChannelTypeCount + (byte)deliveryMethod)); + } + + //Prepare + NetDebug.Write("[RS]Packet: " + property); + + //Check fragmentation + int headerSize = NetPacket.GetHeaderSize(property); + //Save mtu for multithread + int mtu = _mtu; + int length = data.Length; + if (length + headerSize > mtu) + { + //if cannot be fragmented + if (deliveryMethod != DeliveryMethod.ReliableOrdered && deliveryMethod != DeliveryMethod.ReliableUnordered) + throw new TooBigPacketException("Unreliable or ReliableSequenced packet size exceeded maximum of " + (mtu - headerSize) + " bytes, Check allowed size by GetMaxSinglePacketSize()"); + + int packetFullSize = mtu - headerSize; + int packetDataSize = packetFullSize - NetConstants.FragmentHeaderSize; + int totalPackets = length / packetDataSize + (length % packetDataSize == 0 ? 0 : 1); + + if (totalPackets > ushort.MaxValue) + throw new TooBigPacketException("Data was split in " + totalPackets + " fragments, which exceeds " + ushort.MaxValue); + + ushort currentFragmentId = (ushort)Interlocked.Increment(ref _fragmentId); + + for (ushort partIdx = 0; partIdx < totalPackets; partIdx++) + { + int sendLength = length > packetDataSize ? packetDataSize : length; + + NetPacket p = NetManager.PoolGetPacket(headerSize + sendLength + NetConstants.FragmentHeaderSize); + p.Property = property; + p.UserData = userData; + p.FragmentId = currentFragmentId; + p.FragmentPart = partIdx; + p.FragmentsTotal = (ushort)totalPackets; + p.MarkFragmented(); + + data.Slice(partIdx * packetDataSize, sendLength).CopyTo(new Span(p.RawData, NetConstants.FragmentedHeaderTotalSize, sendLength)); + channel.AddToQueue(p); + + length -= sendLength; + } + return; + } + + //Else just send + NetPacket packet = NetManager.PoolGetPacket(headerSize + length); + packet.Property = property; + data.CopyTo(new Span(packet.RawData, headerSize, length)); + packet.UserData = userData; + + if (channel == null) //unreliable + { + lock(_unreliableChannel) + _unreliableChannel.Enqueue(packet); + } + else + { + channel.AddToQueue(packet); + } + } +#endif + + public void Disconnect(byte[] data) + { + NetManager.DisconnectPeer(this, data); + } + + public void Disconnect(NetDataWriter writer) + { + NetManager.DisconnectPeer(this, writer); + } + + public void Disconnect(byte[] data, int start, int count) + { + NetManager.DisconnectPeer(this, data, start, count); + } + + public void Disconnect() + { + NetManager.DisconnectPeer(this); + } + + internal DisconnectResult ProcessDisconnect(NetPacket packet) + { + if ((_connectionState == ConnectionState.Connected || _connectionState == ConnectionState.Outgoing) && + packet.Size >= 9 && + BitConverter.ToInt64(packet.RawData, 1) == _connectTime && + packet.ConnectionNumber == _connectNum) + { + return _connectionState == ConnectionState.Connected + ? DisconnectResult.Disconnect + : DisconnectResult.Reject; + } + return DisconnectResult.None; + } + + internal void AddToReliableChannelSendQueue(BaseChannel channel) + { + _channelSendQueue.Enqueue(channel); + } + + internal ShutdownResult Shutdown(byte[] data, int start, int length, bool force) + { + lock (_shutdownLock) + { + //trying to shutdown already disconnected + if (_connectionState == ConnectionState.Disconnected || + _connectionState == ConnectionState.ShutdownRequested) + { + return ShutdownResult.None; + } + + var result = _connectionState == ConnectionState.Connected + ? ShutdownResult.WasConnected + : ShutdownResult.Success; + + //don't send anything + if (force) + { + _connectionState = ConnectionState.Disconnected; + return result; + } + + //reset time for reconnect protection + Interlocked.Exchange(ref _timeSinceLastPacket, 0); + + //send shutdown packet + _shutdownPacket = new NetPacket(PacketProperty.Disconnect, length) {ConnectionNumber = _connectNum}; + FastBitConverter.GetBytes(_shutdownPacket.RawData, 1, _connectTime); + if (_shutdownPacket.Size >= _mtu) + { + //Drop additional data + NetDebug.WriteError("[Peer] Disconnect additional data size more than MTU - 8!"); + } + else if (data != null && length > 0) + { + Buffer.BlockCopy(data, start, _shutdownPacket.RawData, 9, length); + } + _connectionState = ConnectionState.ShutdownRequested; + NetDebug.Write("[Peer] Send disconnect"); + NetManager.SendRaw(_shutdownPacket, _remoteEndPoint); + return result; + } + } + + private void UpdateRoundTripTime(int roundTripTime) + { + _rtt += roundTripTime; + _rttCount++; + _avgRtt = _rtt/_rttCount; + _resendDelay = 25.0 + _avgRtt * 2.1; // 25 ms + double rtt + } + + internal void AddReliablePacket(DeliveryMethod method, NetPacket p) + { + if (p.IsFragmented) + { + NetDebug.Write("Fragment. Id: {0}, Part: {1}, Total: {2}", p.FragmentId, p.FragmentPart, p.FragmentsTotal); + //Get needed array from dictionary + ushort packetFragId = p.FragmentId; + byte packetChannelId = p.ChannelId; + if (!_holdedFragments.TryGetValue(packetFragId, out var incomingFragments)) + { + incomingFragments = new IncomingFragments + { + Fragments = new NetPacket[p.FragmentsTotal], + ChannelId = p.ChannelId + }; + _holdedFragments.Add(packetFragId, incomingFragments); + } + + //Cache + var fragments = incomingFragments.Fragments; + + //Error check + if (p.FragmentPart >= fragments.Length || + fragments[p.FragmentPart] != null || + p.ChannelId != incomingFragments.ChannelId) + { + NetManager.PoolRecycle(p); + NetDebug.WriteError("Invalid fragment packet"); + return; + } + //Fill array + fragments[p.FragmentPart] = p; + + //Increase received fragments count + incomingFragments.ReceivedCount++; + + //Increase total size + incomingFragments.TotalSize += p.Size - NetConstants.FragmentedHeaderTotalSize; + + //Check for finish + if (incomingFragments.ReceivedCount != fragments.Length) + return; + + //just simple packet + NetPacket resultingPacket = NetManager.PoolGetPacket(incomingFragments.TotalSize); + + int pos = 0; + for (int i = 0; i < incomingFragments.ReceivedCount; i++) + { + var fragment = fragments[i]; + int writtenSize = fragment.Size - NetConstants.FragmentedHeaderTotalSize; + + if (pos+writtenSize > resultingPacket.RawData.Length) + { + _holdedFragments.Remove(packetFragId); + NetDebug.WriteError("Fragment error pos: {0} >= resultPacketSize: {1} , totalSize: {2}", + pos + writtenSize, + resultingPacket.RawData.Length, + incomingFragments.TotalSize); + return; + } + if (fragment.Size > fragment.RawData.Length) + { + _holdedFragments.Remove(packetFragId); + NetDebug.WriteError("Fragment error size: {0} > fragment.RawData.Length: {1}", fragment.Size, fragment.RawData.Length); + return; + } + + //Create resulting big packet + Buffer.BlockCopy( + fragment.RawData, + NetConstants.FragmentedHeaderTotalSize, + resultingPacket.RawData, + pos, + writtenSize); + pos += writtenSize; + + //Free memory + NetManager.PoolRecycle(fragment); + fragments[i] = null; + } + + //Clear memory + _holdedFragments.Remove(packetFragId); + + //Send to process + NetManager.CreateReceiveEvent(resultingPacket, method, (byte)(packetChannelId / NetConstants.ChannelTypeCount), 0, this); + } + else //Just simple packet + { + NetManager.CreateReceiveEvent(p, method, (byte)(p.ChannelId / NetConstants.ChannelTypeCount), NetConstants.ChanneledHeaderSize, this); + } + } + + private void ProcessMtuPacket(NetPacket packet) + { + //header + int + if (packet.Size < NetConstants.PossibleMtu[0]) + return; + + //first stage check (mtu check and mtu ok) + int receivedMtu = BitConverter.ToInt32(packet.RawData, 1); + int endMtuCheck = BitConverter.ToInt32(packet.RawData, packet.Size - 4); + if (receivedMtu != packet.Size || receivedMtu != endMtuCheck || receivedMtu > NetConstants.MaxPacketSize) + { + NetDebug.WriteError("[MTU] Broken packet. RMTU {0}, EMTU {1}, PSIZE {2}", receivedMtu, endMtuCheck, packet.Size); + return; + } + + if (packet.Property == PacketProperty.MtuCheck) + { + _mtuCheckAttempts = 0; + NetDebug.Write("[MTU] check. send back: " + receivedMtu); + packet.Property = PacketProperty.MtuOk; + NetManager.SendRawAndRecycle(packet, _remoteEndPoint); + } + else if(receivedMtu > _mtu && !_finishMtu) //MtuOk + { + //invalid packet + if (receivedMtu != NetConstants.PossibleMtu[_mtuIdx + 1] - NetManager.ExtraPacketSizeForLayer) + return; + + lock (_mtuMutex) + { + SetMtu(_mtuIdx+1); + } + //if maxed - finish. + if (_mtuIdx == NetConstants.PossibleMtu.Length - 1) + _finishMtu = true; + NetManager.PoolRecycle(packet); + NetDebug.Write("[MTU] ok. Increase to: " + _mtu); + } + } + + private void UpdateMtuLogic(int deltaTime) + { + if (_finishMtu) + return; + + _mtuCheckTimer += deltaTime; + if (_mtuCheckTimer < MtuCheckDelay) + return; + + _mtuCheckTimer = 0; + _mtuCheckAttempts++; + if (_mtuCheckAttempts >= MaxMtuCheckAttempts) + { + _finishMtu = true; + return; + } + + lock (_mtuMutex) + { + if (_mtuIdx >= NetConstants.PossibleMtu.Length - 1) + return; + + //Send increased packet + int newMtu = NetConstants.PossibleMtu[_mtuIdx + 1] - NetManager.ExtraPacketSizeForLayer; + var p = NetManager.PoolGetPacket(newMtu); + p.Property = PacketProperty.MtuCheck; + FastBitConverter.GetBytes(p.RawData, 1, newMtu); //place into start + FastBitConverter.GetBytes(p.RawData, p.Size - 4, newMtu);//and end of packet + + //Must check result for MTU fix + if (NetManager.SendRawAndRecycle(p, _remoteEndPoint) <= 0) + _finishMtu = true; + } + } + + internal ConnectRequestResult ProcessConnectRequest(NetConnectRequestPacket connRequest) + { + //current or new request + switch (_connectionState) + { + //P2P case + case ConnectionState.Outgoing: + //fast check + if (connRequest.ConnectionTime < _connectTime) + { + return ConnectRequestResult.P2PLose; + } + //slow rare case check + if (connRequest.ConnectionTime == _connectTime) + { + var remoteBytes = _remoteEndPoint.Serialize(); + var localBytes = connRequest.TargetAddress; + for (int i = remoteBytes.Size-1; i >= 0; i--) + { + byte rb = remoteBytes[i]; + if (rb == localBytes[i]) + continue; + if (rb < localBytes[i]) + return ConnectRequestResult.P2PLose; + } + } + break; + + case ConnectionState.Connected: + //Old connect request + if (connRequest.ConnectionTime == _connectTime) + { + //just reply accept + NetManager.SendRaw(_connectAcceptPacket, _remoteEndPoint); + } + //New connect request + else if (connRequest.ConnectionTime > _connectTime) + { + return ConnectRequestResult.Reconnection; + } + break; + + case ConnectionState.Disconnected: + case ConnectionState.ShutdownRequested: + if (connRequest.ConnectionTime >= _connectTime) + return ConnectRequestResult.NewConnection; + break; + } + return ConnectRequestResult.None; + } + + //Process incoming packet + internal void ProcessPacket(NetPacket packet) + { + //not initialized + if (_connectionState == ConnectionState.Outgoing || _connectionState == ConnectionState.Disconnected) + { + NetManager.PoolRecycle(packet); + return; + } + if (packet.Property == PacketProperty.ShutdownOk) + { + if (_connectionState == ConnectionState.ShutdownRequested) + _connectionState = ConnectionState.Disconnected; + NetManager.PoolRecycle(packet); + return; + } + if (packet.ConnectionNumber != _connectNum) + { + NetDebug.Write(NetLogLevel.Trace, "[RR]Old packet"); + NetManager.PoolRecycle(packet); + return; + } + Interlocked.Exchange(ref _timeSinceLastPacket, 0); + + NetDebug.Write("[RR]PacketProperty: {0}", packet.Property); + switch (packet.Property) + { + case PacketProperty.Merged: + int pos = NetConstants.HeaderSize; + while (pos < packet.Size) + { + ushort size = BitConverter.ToUInt16(packet.RawData, pos); + pos += 2; + if (packet.RawData.Length - pos < size) + break; + + NetPacket mergedPacket = NetManager.PoolGetPacket(size); + Buffer.BlockCopy(packet.RawData, pos, mergedPacket.RawData, 0, size); + mergedPacket.Size = size; + + if (!mergedPacket.Verify()) + break; + + pos += size; + ProcessPacket(mergedPacket); + } + NetManager.PoolRecycle(packet); + break; + //If we get ping, send pong + case PacketProperty.Ping: + if (NetUtils.RelativeSequenceNumber(packet.Sequence, _pongPacket.Sequence) > 0) + { + NetDebug.Write("[PP]Ping receive, send pong"); + FastBitConverter.GetBytes(_pongPacket.RawData, 3, DateTime.UtcNow.Ticks); + _pongPacket.Sequence = packet.Sequence; + NetManager.SendRaw(_pongPacket, _remoteEndPoint); + } + NetManager.PoolRecycle(packet); + break; + + //If we get pong, calculate ping time and rtt + case PacketProperty.Pong: + if (packet.Sequence == _pingPacket.Sequence) + { + _pingTimer.Stop(); + int elapsedMs = (int)_pingTimer.ElapsedMilliseconds; + _remoteDelta = BitConverter.ToInt64(packet.RawData, 3) + (elapsedMs * TimeSpan.TicksPerMillisecond ) / 2 - DateTime.UtcNow.Ticks; + UpdateRoundTripTime(elapsedMs); + NetManager.ConnectionLatencyUpdated(this, elapsedMs / 2); + NetDebug.Write("[PP]Ping: {0} - {1} - {2}", packet.Sequence, elapsedMs, _remoteDelta); + } + NetManager.PoolRecycle(packet); + break; + + case PacketProperty.Ack: + case PacketProperty.Channeled: + if (packet.ChannelId > _channels.Length) + { + NetManager.PoolRecycle(packet); + break; + } + var channel = _channels[packet.ChannelId] ?? (packet.Property == PacketProperty.Ack ? null : CreateChannel(packet.ChannelId)); + if (channel != null) + { + if (!channel.ProcessPacket(packet)) + NetManager.PoolRecycle(packet); + } + break; + + //Simple packet without acks + case PacketProperty.Unreliable: + NetManager.CreateReceiveEvent(packet, DeliveryMethod.Unreliable, 0, NetConstants.HeaderSize, this); + return; + + case PacketProperty.MtuCheck: + case PacketProperty.MtuOk: + ProcessMtuPacket(packet); + break; + + default: + NetDebug.WriteError("Error! Unexpected packet type: " + packet.Property); + break; + } + } + + private void SendMerged() + { + if (_mergeCount == 0) + return; + int bytesSent; + if (_mergeCount > 1) + { + NetDebug.Write("[P]Send merged: " + _mergePos + ", count: " + _mergeCount); + bytesSent = NetManager.SendRaw(_mergeData.RawData, 0, NetConstants.HeaderSize + _mergePos, _remoteEndPoint); + } + else + { + //Send without length information and merging + bytesSent = NetManager.SendRaw(_mergeData.RawData, NetConstants.HeaderSize + 2, _mergePos - 2, _remoteEndPoint); + } + + if (NetManager.EnableStatistics) + { + Statistics.IncrementPacketsSent(); + Statistics.AddBytesSent(bytesSent); + } + + _mergePos = 0; + _mergeCount = 0; + } + + internal void SendUserData(NetPacket packet) + { + packet.ConnectionNumber = _connectNum; + int mergedPacketSize = NetConstants.HeaderSize + packet.Size + 2; + const int sizeTreshold = 20; + if (mergedPacketSize + sizeTreshold >= _mtu) + { + NetDebug.Write(NetLogLevel.Trace, "[P]SendingPacket: " + packet.Property); + int bytesSent = NetManager.SendRaw(packet, _remoteEndPoint); + + if (NetManager.EnableStatistics) + { + Statistics.IncrementPacketsSent(); + Statistics.AddBytesSent(bytesSent); + } + + return; + } + if (_mergePos + mergedPacketSize > _mtu) + SendMerged(); + + FastBitConverter.GetBytes(_mergeData.RawData, _mergePos + NetConstants.HeaderSize, (ushort)packet.Size); + Buffer.BlockCopy(packet.RawData, 0, _mergeData.RawData, _mergePos + NetConstants.HeaderSize + 2, packet.Size); + _mergePos += packet.Size + 2; + _mergeCount++; + //DebugWriteForce("Merged: " + _mergePos + "/" + (_mtu - 2) + ", count: " + _mergeCount); + } + + internal void Update(int deltaTime) + { + Interlocked.Add(ref _timeSinceLastPacket, deltaTime); + switch (_connectionState) + { + case ConnectionState.Connected: + if (_timeSinceLastPacket > NetManager.DisconnectTimeout) + { + NetDebug.Write( + "[UPDATE] Disconnect by timeout: {0} > {1}", + _timeSinceLastPacket, + NetManager.DisconnectTimeout); + NetManager.DisconnectPeerForce(this, DisconnectReason.Timeout, 0, null); + return; + } + break; + + case ConnectionState.ShutdownRequested: + if (_timeSinceLastPacket > NetManager.DisconnectTimeout) + { + _connectionState = ConnectionState.Disconnected; + } + else + { + _shutdownTimer += deltaTime; + if (_shutdownTimer >= ShutdownDelay) + { + _shutdownTimer = 0; + NetManager.SendRaw(_shutdownPacket, _remoteEndPoint); + } + } + return; + + case ConnectionState.Outgoing: + _connectTimer += deltaTime; + if (_connectTimer > NetManager.ReconnectDelay) + { + _connectTimer = 0; + _connectAttempts++; + if (_connectAttempts > NetManager.MaxConnectAttempts) + { + NetManager.DisconnectPeerForce(this, DisconnectReason.ConnectionFailed, 0, null); + return; + } + + //else send connect again + NetManager.SendRaw(_connectRequestPacket, _remoteEndPoint); + } + return; + + case ConnectionState.Disconnected: + return; + } + + //Send ping + _pingSendTimer += deltaTime; + if (_pingSendTimer >= NetManager.PingInterval) + { + NetDebug.Write("[PP] Send ping..."); + //reset timer + _pingSendTimer = 0; + //send ping + _pingPacket.Sequence++; + //ping timeout + if (_pingTimer.IsRunning) + UpdateRoundTripTime((int)_pingTimer.ElapsedMilliseconds); + _pingTimer.Restart(); + NetManager.SendRaw(_pingPacket, _remoteEndPoint); + } + + //RTT - round trip time + _rttResetTimer += deltaTime; + if (_rttResetTimer >= NetManager.PingInterval * 3) + { + _rttResetTimer = 0; + _rtt = _avgRtt; + _rttCount = 1; + } + + UpdateMtuLogic(deltaTime); + + //Pending send + int count = _channelSendQueue.Count; + while (count-- > 0) + { + if (!_channelSendQueue.TryDequeue(out var channel)) + break; + if (channel.SendAndCheckQueue()) + { + // still has something to send, re-add it to the send queue + _channelSendQueue.Enqueue(channel); + } + } + + lock (_unreliableChannel) + { + int unreliableCount = _unreliableChannel.Count; + for (int i = 0; i < unreliableCount; i++) + { + var packet = _unreliableChannel.Dequeue(); + SendUserData(packet); + NetManager.PoolRecycle(packet); + } + } + + SendMerged(); + } + + //For reliable channel + internal void RecycleAndDeliver(NetPacket packet) + { + if (packet.UserData != null) + { + if (packet.IsFragmented) + { + _deliveredFragments.TryGetValue(packet.FragmentId, out ushort fragCount); + fragCount++; + if (fragCount == packet.FragmentsTotal) + { + NetManager.MessageDelivered(this, packet.UserData); + _deliveredFragments.Remove(packet.FragmentId); + } + else + { + _deliveredFragments[packet.FragmentId] = fragCount; + } + } + else + { + NetManager.MessageDelivered(this, packet.UserData); + } + packet.UserData = null; + } + NetManager.PoolRecycle(packet); + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPeer.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPeer.cs.meta new file mode 100644 index 0000000..7d06b87 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPeer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3e1a9277334c51545b92369c8e4c6d74 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetStatistics.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetStatistics.cs new file mode 100644 index 0000000..032e275 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetStatistics.cs @@ -0,0 +1,81 @@ +using System.Threading; + +namespace LiteNetLib +{ + public sealed class NetStatistics + { + private long _packetsSent; + private long _packetsReceived; + private long _bytesSent; + private long _bytesReceived; + private long _packetLoss; + + public long PacketsSent => Interlocked.Read(ref _packetsSent); + public long PacketsReceived => Interlocked.Read(ref _packetsReceived); + public long BytesSent => Interlocked.Read(ref _bytesSent); + public long BytesReceived => Interlocked.Read(ref _bytesReceived); + public long PacketLoss => Interlocked.Read(ref _packetLoss); + + public long PacketLossPercent + { + get + { + long sent = PacketsSent, loss = PacketLoss; + + return sent == 0 ? 0 : loss * 100 / sent; + } + } + + public void Reset() + { + Interlocked.Exchange(ref _packetsSent, 0); + Interlocked.Exchange(ref _packetsReceived, 0); + Interlocked.Exchange(ref _bytesSent, 0); + Interlocked.Exchange(ref _bytesReceived, 0); + Interlocked.Exchange(ref _packetLoss, 0); + } + + public void IncrementPacketsSent() + { + Interlocked.Increment(ref _packetsSent); + } + + public void IncrementPacketsReceived() + { + Interlocked.Increment(ref _packetsReceived); + } + + public void AddBytesSent(long bytesSent) + { + Interlocked.Add(ref _bytesSent, bytesSent); + } + + public void AddBytesReceived(long bytesReceived) + { + Interlocked.Add(ref _bytesReceived, bytesReceived); + } + + public void IncrementPacketLoss() + { + Interlocked.Increment(ref _packetLoss); + } + + public void AddPacketLoss(long packetLoss) + { + Interlocked.Add(ref _packetLoss, packetLoss); + } + + public override string ToString() + { + return + string.Format( + "BytesReceived: {0}\nPacketsReceived: {1}\nBytesSent: {2}\nPacketsSent: {3}\nPacketLoss: {4}\nPacketLossPercent: {5}\n", + BytesReceived, + PacketsReceived, + BytesSent, + PacketsSent, + PacketLoss, + PacketLossPercent); + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetStatistics.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetStatistics.cs.meta new file mode 100644 index 0000000..8d54a07 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetStatistics.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 15d4b62077bda58428b77d57fa4a9288 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetUtils.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetUtils.cs new file mode 100644 index 0000000..d162b2d --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetUtils.cs @@ -0,0 +1,194 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Net.NetworkInformation; + +namespace LiteNetLib +{ + /// + /// Address type that you want to receive from NetUtils.GetLocalIp method + /// + [Flags] + public enum LocalAddrType + { + IPv4 = 1, + IPv6 = 2, + All = IPv4 | IPv6 + } + + /// + /// Some specific network utilities + /// + public static class NetUtils + { + public static IPEndPoint MakeEndPoint(string hostStr, int port) + { + return new IPEndPoint(ResolveAddress(hostStr), port); + } + + public static IPAddress ResolveAddress(string hostStr) + { + if(hostStr == "localhost") + return IPAddress.Loopback; + + if (!IPAddress.TryParse(hostStr, out var ipAddress)) + { + if (NetManager.IPv6Support) + ipAddress = ResolveAddress(hostStr, AddressFamily.InterNetworkV6); + if (ipAddress == null) + ipAddress = ResolveAddress(hostStr, AddressFamily.InterNetwork); + } + if (ipAddress == null) + throw new ArgumentException("Invalid address: " + hostStr); + + return ipAddress; + } + + public static IPAddress ResolveAddress(string hostStr, AddressFamily addressFamily) + { + IPAddress[] addresses = Dns.GetHostEntry(hostStr).AddressList; + foreach (IPAddress ip in addresses) + { + if (ip.AddressFamily == addressFamily) + { + return ip; + } + } + return null; + } + + /// + /// Get all local ip addresses + /// + /// type of address (IPv4, IPv6 or both) + /// List with all local ip addresses + public static List GetLocalIpList(LocalAddrType addrType) + { + List targetList = new List(); + GetLocalIpList(targetList, addrType); + return targetList; + } + + /// + /// Get all local ip addresses (non alloc version) + /// + /// result list + /// type of address (IPv4, IPv6 or both) + public static void GetLocalIpList(IList targetList, LocalAddrType addrType) + { + bool ipv4 = (addrType & LocalAddrType.IPv4) == LocalAddrType.IPv4; + bool ipv6 = (addrType & LocalAddrType.IPv6) == LocalAddrType.IPv6; + try + { + foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces()) + { + //Skip loopback and disabled network interfaces + if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback || + ni.OperationalStatus != OperationalStatus.Up) + continue; + + var ipProps = ni.GetIPProperties(); + + //Skip address without gateway + if (ipProps.GatewayAddresses.Count == 0) + continue; + + foreach (UnicastIPAddressInformation ip in ipProps.UnicastAddresses) + { + var address = ip.Address; + if ((ipv4 && address.AddressFamily == AddressFamily.InterNetwork) || + (ipv6 && address.AddressFamily == AddressFamily.InterNetworkV6)) + targetList.Add(address.ToString()); + } + } + + //Fallback mode (unity android) + if (targetList.Count == 0) + { + IPAddress[] addresses = Dns.GetHostEntry(Dns.GetHostName()).AddressList; + foreach (IPAddress ip in addresses) + { + if((ipv4 && ip.AddressFamily == AddressFamily.InterNetwork) || + (ipv6 && ip.AddressFamily == AddressFamily.InterNetworkV6)) + targetList.Add(ip.ToString()); + } + } + } + catch + { + //ignored + } + + if (targetList.Count == 0) + { + if(ipv4) + targetList.Add("127.0.0.1"); + if(ipv6) + targetList.Add("::1"); + } + } + + private static readonly List IpList = new List(); + /// + /// Get first detected local ip address + /// + /// type of address (IPv4, IPv6 or both) + /// IP address if available. Else - string.Empty + public static string GetLocalIp(LocalAddrType addrType) + { + lock (IpList) + { + IpList.Clear(); + GetLocalIpList(IpList, addrType); + return IpList.Count == 0 ? string.Empty : IpList[0]; + } + } + + // =========================================== + // Internal and debug log related stuff + // =========================================== + internal static void PrintInterfaceInfos() + { + NetDebug.WriteForce(NetLogLevel.Info, "IPv6Support: {0}", NetManager.IPv6Support); + try + { + foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces()) + { + foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses) + { + if (ip.Address.AddressFamily == AddressFamily.InterNetwork || + ip.Address.AddressFamily == AddressFamily.InterNetworkV6) + { + NetDebug.WriteForce( + NetLogLevel.Info, + "Interface: {0}, Type: {1}, Ip: {2}, OpStatus: {3}", + ni.Name, + ni.NetworkInterfaceType.ToString(), + ip.Address.ToString(), + ni.OperationalStatus.ToString()); + } + } + } + } + catch (Exception e) + { + NetDebug.WriteForce(NetLogLevel.Info, "Error while getting interface infos: {0}", e.ToString()); + } + } + + internal static int RelativeSequenceNumber(int number, int expected) + { + return (number - expected + NetConstants.MaxSequence + NetConstants.HalfMaxSequence) % NetConstants.MaxSequence - NetConstants.HalfMaxSequence; + } + + internal static T[] AllocatePinnedUninitializedArray(int count) where T : unmanaged + { +#if NET5_0_OR_GREATER || NET5_0 + return GC.AllocateUninitializedArray(count, true); +#else + return new T[count]; +#endif + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetUtils.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetUtils.cs.meta new file mode 100644 index 0000000..0d039e7 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ab6438405a73f8c46ac22cd2f259a0e2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PausedSocketFix.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PausedSocketFix.cs new file mode 100644 index 0000000..2ecbfb8 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PausedSocketFix.cs @@ -0,0 +1,75 @@ + +using System.Net; + +namespace LiteNetLib +{ + + public class PausedSocketFix + { + public bool ApplicationFocused { get; private set; } + + private NetManager _netManager; + private IPAddress _ipv4; + private IPAddress _ipv6; + private int _port; + private bool _manualMode; + + + public PausedSocketFix() + { + UnityEngine.Application.focusChanged += Application_focusChanged; + } + public PausedSocketFix(NetManager netManager, IPAddress ipv4, IPAddress ipv6, int port, bool manualMode) : this() + { + Initialize(netManager, ipv4, ipv6, port, manualMode); + } + + ~PausedSocketFix() + { + UnityEngine.Application.focusChanged -= Application_focusChanged; + } + + public void Initialize(NetManager netManager, IPAddress ipv4, IPAddress ipv6, int port, bool manualMode) + { + _netManager = netManager; + _ipv4 = ipv4; + _ipv6 = ipv6; + _port = port; + _manualMode = manualMode; + } + + + private void Application_focusChanged(bool focused) + { + ApplicationFocused = focused; + //If coming back into focus see if a reconnect is needed. + if (focused) + TryReconnect(); + } + + + private void TryReconnect() + { + if (_netManager == null) + return; + //Was intentionally disconnected at some point. + if (!_netManager.IsRunning) + return; + if (!_netManager.IsClient) + return; + //Socket is still running. + if (_netManager.SocketActive(false) || _netManager.SocketActive(true)) + return; + + //Socket isn't running but should be. Try to start again. + if (!_netManager.Start(_ipv4, _ipv6, _port, _manualMode)) + { + NetDebug.WriteError($"[S] Cannot restore connection. Ipv4 {_ipv4}, Ipv6 {_ipv6}, Port {_port}, ManualMode {_manualMode}"); + _netManager.CloseSocket(false); + } + } + + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PausedSocketFix.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PausedSocketFix.cs.meta new file mode 100644 index 0000000..18fc877 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PausedSocketFix.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7d69976ae1e5f344daec35f343bbe189 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PooledPacket.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PooledPacket.cs new file mode 100644 index 0000000..26ef7bd --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PooledPacket.cs @@ -0,0 +1,32 @@ +namespace LiteNetLib +{ + public readonly ref struct PooledPacket + { + internal readonly NetPacket _packet; + internal readonly byte _channelNumber; + + /// + /// Maximum data size that you can put into such packet + /// + public readonly int MaxUserDataSize; + + /// + /// Offset for user data when writing to Data array + /// + public readonly int UserDataOffset; + + /// + /// Raw packet data. Do not modify header! Use UserDataOffset as start point for your data + /// + public byte[] Data => _packet.RawData; + + internal PooledPacket(NetPacket packet, int maxDataSize, byte channelNumber) + { + _packet = packet; + UserDataOffset = _packet.GetHeaderSize(); + _packet.Size = UserDataOffset; + MaxUserDataSize = maxDataSize - UserDataOffset; + _channelNumber = channelNumber; + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PooledPacket.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PooledPacket.cs.meta new file mode 100644 index 0000000..8c3b978 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PooledPacket.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 76e47a542bb12e043b874c7180d98319 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ReliableChannel.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ReliableChannel.cs new file mode 100644 index 0000000..f825e37 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ReliableChannel.cs @@ -0,0 +1,336 @@ +using System; + +namespace LiteNetLib +{ + internal sealed class ReliableChannel : BaseChannel + { + private struct PendingPacket + { + private NetPacket _packet; + private long _timeStamp; + private bool _isSent; + + public override string ToString() + { + return _packet == null ? "Empty" : _packet.Sequence.ToString(); + } + + public void Init(NetPacket packet) + { + _packet = packet; + _isSent = false; + } + + //Returns true if there is a pending packet inside + public bool TrySend(long currentTime, NetPeer peer) + { + if (_packet == null) + return false; + + if (_isSent) //check send time + { + double resendDelay = peer.ResendDelay * TimeSpan.TicksPerMillisecond; + double packetHoldTime = currentTime - _timeStamp; + if (packetHoldTime < resendDelay) + return true; + NetDebug.Write("[RC]Resend: {0} > {1}", (int)packetHoldTime, resendDelay); + } + _timeStamp = currentTime; + _isSent = true; + peer.SendUserData(_packet); + return true; + } + + public bool Clear(NetPeer peer) + { + if (_packet != null) + { + peer.RecycleAndDeliver(_packet); + _packet = null; + return true; + } + return false; + } + } + + private readonly NetPacket _outgoingAcks; //for send acks + private readonly PendingPacket[] _pendingPackets; //for unacked packets and duplicates + private readonly NetPacket[] _receivedPackets; //for order + private readonly bool[] _earlyReceived; //for unordered + + private int _localSeqence; + private int _remoteSequence; + private int _localWindowStart; + private int _remoteWindowStart; + + private bool _mustSendAcks; + + private readonly DeliveryMethod _deliveryMethod; + private readonly bool _ordered; + private readonly int _windowSize; + private const int BitsInByte = 8; + private readonly byte _id; + + public ReliableChannel(NetPeer peer, bool ordered, byte id) : base(peer) + { + _id = id; + _windowSize = NetConstants.DefaultWindowSize; + _ordered = ordered; + _pendingPackets = new PendingPacket[_windowSize]; + for (int i = 0; i < _pendingPackets.Length; i++) + _pendingPackets[i] = new PendingPacket(); + + if (_ordered) + { + _deliveryMethod = DeliveryMethod.ReliableOrdered; + _receivedPackets = new NetPacket[_windowSize]; + } + else + { + _deliveryMethod = DeliveryMethod.ReliableUnordered; + _earlyReceived = new bool[_windowSize]; + } + + _localWindowStart = 0; + _localSeqence = 0; + _remoteSequence = 0; + _remoteWindowStart = 0; + _outgoingAcks = new NetPacket(PacketProperty.Ack, (_windowSize - 1) / BitsInByte + 2) {ChannelId = id}; + } + + //ProcessAck in packet + private void ProcessAck(NetPacket packet) + { + if (packet.Size != _outgoingAcks.Size) + { + NetDebug.Write("[PA]Invalid acks packet size"); + return; + } + + ushort ackWindowStart = packet.Sequence; + int windowRel = NetUtils.RelativeSequenceNumber(_localWindowStart, ackWindowStart); + if (ackWindowStart >= NetConstants.MaxSequence || windowRel < 0) + { + NetDebug.Write("[PA]Bad window start"); + return; + } + + //check relevance + if (windowRel >= _windowSize) + { + NetDebug.Write("[PA]Old acks"); + return; + } + + byte[] acksData = packet.RawData; + lock (_pendingPackets) + { + for (int pendingSeq = _localWindowStart; + pendingSeq != _localSeqence; + pendingSeq = (pendingSeq + 1) % NetConstants.MaxSequence) + { + int rel = NetUtils.RelativeSequenceNumber(pendingSeq, ackWindowStart); + if (rel >= _windowSize) + { + NetDebug.Write("[PA]REL: " + rel); + break; + } + + int pendingIdx = pendingSeq % _windowSize; + int currentByte = NetConstants.ChanneledHeaderSize + pendingIdx / BitsInByte; + int currentBit = pendingIdx % BitsInByte; + if ((acksData[currentByte] & (1 << currentBit)) == 0) + { + if (Peer.NetManager.EnableStatistics) + { + Peer.Statistics.IncrementPacketLoss(); + Peer.NetManager.Statistics.IncrementPacketLoss(); + } + + //Skip false ack + NetDebug.Write("[PA]False ack: {0}", pendingSeq); + continue; + } + + if (pendingSeq == _localWindowStart) + { + //Move window + _localWindowStart = (_localWindowStart + 1) % NetConstants.MaxSequence; + } + + //clear packet + if (_pendingPackets[pendingIdx].Clear(Peer)) + NetDebug.Write("[PA]Removing reliableInOrder ack: {0} - true", pendingSeq); + } + } + } + + protected override bool SendNextPackets() + { + if (_mustSendAcks) + { + _mustSendAcks = false; + NetDebug.Write("[RR]SendAcks"); + lock(_outgoingAcks) + Peer.SendUserData(_outgoingAcks); + } + + long currentTime = DateTime.UtcNow.Ticks; + bool hasPendingPackets = false; + + lock (_pendingPackets) + { + //get packets from queue + while (!OutgoingQueue.IsEmpty) + { + int relate = NetUtils.RelativeSequenceNumber(_localSeqence, _localWindowStart); + if (relate >= _windowSize) + break; + + if (!OutgoingQueue.TryDequeue(out var netPacket)) + break; + + netPacket.Sequence = (ushort) _localSeqence; + netPacket.ChannelId = _id; + _pendingPackets[_localSeqence % _windowSize].Init(netPacket); + _localSeqence = (_localSeqence + 1) % NetConstants.MaxSequence; + } + + //send + for (int pendingSeq = _localWindowStart; pendingSeq != _localSeqence; pendingSeq = (pendingSeq + 1) % NetConstants.MaxSequence) + { + // Please note: TrySend is invoked on a mutable struct, it's important to not extract it into a variable here + if (_pendingPackets[pendingSeq % _windowSize].TrySend(currentTime, Peer)) + hasPendingPackets = true; + } + } + + return hasPendingPackets || _mustSendAcks || !OutgoingQueue.IsEmpty; + } + + //Process incoming packet + public override bool ProcessPacket(NetPacket packet) + { + if (packet.Property == PacketProperty.Ack) + { + ProcessAck(packet); + return false; + } + int seq = packet.Sequence; + if (seq >= NetConstants.MaxSequence) + { + NetDebug.Write("[RR]Bad sequence"); + return false; + } + + int relate = NetUtils.RelativeSequenceNumber(seq, _remoteWindowStart); + int relateSeq = NetUtils.RelativeSequenceNumber(seq, _remoteSequence); + + if (relateSeq > _windowSize) + { + NetDebug.Write("[RR]Bad sequence"); + return false; + } + + //Drop bad packets + if (relate < 0) + { + //Too old packet doesn't ack + NetDebug.Write("[RR]ReliableInOrder too old"); + return false; + } + if (relate >= _windowSize * 2) + { + //Some very new packet + NetDebug.Write("[RR]ReliableInOrder too new"); + return false; + } + + //If very new - move window + int ackIdx; + int ackByte; + int ackBit; + lock (_outgoingAcks) + { + if (relate >= _windowSize) + { + //New window position + int newWindowStart = (_remoteWindowStart + relate - _windowSize + 1) % NetConstants.MaxSequence; + _outgoingAcks.Sequence = (ushort) newWindowStart; + + //Clean old data + while (_remoteWindowStart != newWindowStart) + { + ackIdx = _remoteWindowStart % _windowSize; + ackByte = NetConstants.ChanneledHeaderSize + ackIdx / BitsInByte; + ackBit = ackIdx % BitsInByte; + _outgoingAcks.RawData[ackByte] &= (byte) ~(1 << ackBit); + _remoteWindowStart = (_remoteWindowStart + 1) % NetConstants.MaxSequence; + } + } + + //Final stage - process valid packet + //trigger acks send + _mustSendAcks = true; + + ackIdx = seq % _windowSize; + ackByte = NetConstants.ChanneledHeaderSize + ackIdx / BitsInByte; + ackBit = ackIdx % BitsInByte; + if ((_outgoingAcks.RawData[ackByte] & (1 << ackBit)) != 0) + { + NetDebug.Write("[RR]ReliableInOrder duplicate"); + //because _mustSendAcks == true + AddToPeerChannelSendQueue(); + return false; + } + + //save ack + _outgoingAcks.RawData[ackByte] |= (byte) (1 << ackBit); + } + + AddToPeerChannelSendQueue(); + + //detailed check + if (seq == _remoteSequence) + { + NetDebug.Write("[RR]ReliableInOrder packet succes"); + Peer.AddReliablePacket(_deliveryMethod, packet); + _remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence; + + if (_ordered) + { + NetPacket p; + while ((p = _receivedPackets[_remoteSequence % _windowSize]) != null) + { + //process holden packet + _receivedPackets[_remoteSequence % _windowSize] = null; + Peer.AddReliablePacket(_deliveryMethod, p); + _remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence; + } + } + else + { + while (_earlyReceived[_remoteSequence % _windowSize]) + { + //process early packet + _earlyReceived[_remoteSequence % _windowSize] = false; + _remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence; + } + } + return true; + } + + //holden packet + if (_ordered) + { + _receivedPackets[ackIdx] = packet; + } + else + { + _earlyReceived[ackIdx] = true; + Peer.AddReliablePacket(_deliveryMethod, packet); + } + return true; + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ReliableChannel.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ReliableChannel.cs.meta new file mode 100644 index 0000000..55f9b0b --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ReliableChannel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f7e20c169333af54fa233b0d70b19a61 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/SequencedChannel.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/SequencedChannel.cs new file mode 100644 index 0000000..a3763ee --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/SequencedChannel.cs @@ -0,0 +1,110 @@ +using System; + +namespace LiteNetLib +{ + internal sealed class SequencedChannel : BaseChannel + { + private int _localSequence; + private ushort _remoteSequence; + private readonly bool _reliable; + private NetPacket _lastPacket; + private readonly NetPacket _ackPacket; + private bool _mustSendAck; + private readonly byte _id; + private long _lastPacketSendTime; + + public SequencedChannel(NetPeer peer, bool reliable, byte id) : base(peer) + { + _id = id; + _reliable = reliable; + if (_reliable) + _ackPacket = new NetPacket(PacketProperty.Ack, 0) {ChannelId = id}; + } + + protected override bool SendNextPackets() + { + if (_reliable && OutgoingQueue.Count == 0) + { + long currentTime = DateTime.UtcNow.Ticks; + long packetHoldTime = currentTime - _lastPacketSendTime; + if (packetHoldTime >= Peer.ResendDelay * TimeSpan.TicksPerMillisecond) + { + var packet = _lastPacket; + if (packet != null) + { + _lastPacketSendTime = currentTime; + Peer.SendUserData(packet); + } + } + } + else + { + while (OutgoingQueue.TryDequeue(out var packet)) + { + _localSequence = (_localSequence + 1) % NetConstants.MaxSequence; + packet.Sequence = (ushort)_localSequence; + packet.ChannelId = _id; + Peer.SendUserData(packet); + + if (_reliable && OutgoingQueue.Count == 0) + { + _lastPacketSendTime = DateTime.UtcNow.Ticks; + _lastPacket = packet; + } + else + { + Peer.NetManager.PoolRecycle(packet); + } + } + } + + if (_reliable && _mustSendAck) + { + _mustSendAck = false; + _ackPacket.Sequence = _remoteSequence; + Peer.SendUserData(_ackPacket); + } + + return _lastPacket != null; + } + + public override bool ProcessPacket(NetPacket packet) + { + if (packet.IsFragmented) + return false; + if (packet.Property == PacketProperty.Ack) + { + if (_reliable && _lastPacket != null && packet.Sequence == _lastPacket.Sequence) + _lastPacket = null; + return false; + } + int relative = NetUtils.RelativeSequenceNumber(packet.Sequence, _remoteSequence); + bool packetProcessed = false; + if (packet.Sequence < NetConstants.MaxSequence && relative > 0) + { + if (Peer.NetManager.EnableStatistics) + { + Peer.Statistics.AddPacketLoss(relative - 1); + Peer.NetManager.Statistics.AddPacketLoss(relative - 1); + } + + _remoteSequence = packet.Sequence; + Peer.NetManager.CreateReceiveEvent( + packet, + _reliable ? DeliveryMethod.ReliableSequenced : DeliveryMethod.Sequenced, + (byte)(packet.ChannelId / NetConstants.ChannelTypeCount), + NetConstants.ChanneledHeaderSize, + Peer); + packetProcessed = true; + } + + if (_reliable) + { + _mustSendAck = true; + AddToPeerChannelSendQueue(); + } + + return packetProcessed; + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/SequencedChannel.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/SequencedChannel.cs.meta new file mode 100644 index 0000000..a6b7dba --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/SequencedChannel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c4e1f97908b70e642baea0fc22724282 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils.meta new file mode 100644 index 0000000..5c1f750 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0c6e0e6809efaf74c8c6864f8129b89f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/CRC32C.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/CRC32C.cs new file mode 100644 index 0000000..7e85680 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/CRC32C.cs @@ -0,0 +1,150 @@ +#if NETCOREAPP3_0_OR_GREATER || NETCOREAPP3_1 || NET5_0 +using System; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics.X86; +#endif +#if NET5_0_OR_GREATER || NET5_0 +using System.Runtime.Intrinsics.Arm; +#endif + +namespace LiteNetLib.Utils +{ + //Implementation from Crc32.NET + public static class CRC32C + { + public const int ChecksumSize = 4; + private const uint Poly = 0x82F63B78u; + private static readonly uint[] Table; + + static CRC32C() + { +#if NETCOREAPP3_0_OR_GREATER || NETCOREAPP3_1 || NET5_0 + if (Sse42.IsSupported) + return; +#endif +#if NET5_0_OR_GREATER || NET5_0 + if (Crc32.IsSupported) + return; +#endif + Table = NetUtils.AllocatePinnedUninitializedArray(16 * 256); + for (uint i = 0; i < 256; i++) + { + uint res = i; + for (int t = 0; t < 16; t++) + { + for (int k = 0; k < 8; k++) + res = (res & 1) == 1 ? Poly ^ (res >> 1) : (res >> 1); + Table[t * 256 + i] = res; + } + } + } + + /// + /// Compute CRC32C for data + /// + /// input data + /// offset + /// length + /// CRC32C checksum + public static uint Compute(byte[] input, int offset, int length) + { + uint crcLocal = uint.MaxValue; +#if NETCOREAPP3_0_OR_GREATER || NETCOREAPP3_1 || NET5_0 + if (Sse42.IsSupported) + { + var data = new ReadOnlySpan(input, offset, length); + int processed = 0; + if (Sse42.X64.IsSupported && data.Length > sizeof(ulong)) + { + processed = data.Length / sizeof(ulong) * sizeof(ulong); + var ulongs = MemoryMarshal.Cast(data.Slice(0, processed)); + ulong crclong = crcLocal; + for (int i = 0; i < ulongs.Length; i++) + { + crclong = Sse42.X64.Crc32(crclong, ulongs[i]); + } + + crcLocal = (uint)crclong; + } + else if (data.Length > sizeof(uint)) + { + processed = data.Length / sizeof(uint) * sizeof(uint); + var uints = MemoryMarshal.Cast(data.Slice(0, processed)); + for (int i = 0; i < uints.Length; i++) + { + crcLocal = Sse42.Crc32(crcLocal, uints[i]); + } + } + + for (int i = processed; i < data.Length; i++) + { + crcLocal = Sse42.Crc32(crcLocal, data[i]); + } + + return crcLocal ^ uint.MaxValue; + } +#endif +#if NET5_0_OR_GREATER || NET5_0 + if (Crc32.IsSupported) + { + var data = new ReadOnlySpan(input, offset, length); + int processed = 0; + if (Crc32.Arm64.IsSupported && data.Length > sizeof(ulong)) + { + processed = data.Length / sizeof(ulong) * sizeof(ulong); + var ulongs = MemoryMarshal.Cast(data.Slice(0, processed)); + for (int i = 0; i < ulongs.Length; i++) + { + crcLocal = Crc32.Arm64.ComputeCrc32C(crcLocal, ulongs[i]); + } + } + else if (data.Length > sizeof(uint)) + { + processed = data.Length / sizeof(uint) * sizeof(uint); + var uints = MemoryMarshal.Cast(data.Slice(0, processed)); + for (int i = 0; i < uints.Length; i++) + { + crcLocal = Crc32.ComputeCrc32C(crcLocal, uints[i]); + } + } + + for (int i = processed; i < data.Length; i++) + { + crcLocal = Crc32.ComputeCrc32C(crcLocal, data[i]); + } + + return crcLocal ^ uint.MaxValue; + } +#endif + while (length >= 16) + { + var a = Table[(3 * 256) + input[offset + 12]] + ^ Table[(2 * 256) + input[offset + 13]] + ^ Table[(1 * 256) + input[offset + 14]] + ^ Table[(0 * 256) + input[offset + 15]]; + + var b = Table[(7 * 256) + input[offset + 8]] + ^ Table[(6 * 256) + input[offset + 9]] + ^ Table[(5 * 256) + input[offset + 10]] + ^ Table[(4 * 256) + input[offset + 11]]; + + var c = Table[(11 * 256) + input[offset + 4]] + ^ Table[(10 * 256) + input[offset + 5]] + ^ Table[(9 * 256) + input[offset + 6]] + ^ Table[(8 * 256) + input[offset + 7]]; + + var d = Table[(15 * 256) + ((byte)crcLocal ^ input[offset])] + ^ Table[(14 * 256) + ((byte)(crcLocal >> 8) ^ input[offset + 1])] + ^ Table[(13 * 256) + ((byte)(crcLocal >> 16) ^ input[offset + 2])] + ^ Table[(12 * 256) + ((crcLocal >> 24) ^ input[offset + 3])]; + + crcLocal = d ^ c ^ b ^ a; + offset += 16; + length -= 16; + } + while (--length >= 0) + crcLocal = Table[(byte)(crcLocal ^ input[offset++])] ^ crcLocal >> 8; + return crcLocal ^ uint.MaxValue; + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/CRC32C.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/CRC32C.cs.meta new file mode 100644 index 0000000..d42bd93 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/CRC32C.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 912ba506b0945b743be5c4129177024c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/FastBitConverter.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/FastBitConverter.cs new file mode 100644 index 0000000..3ecd10c --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/FastBitConverter.cs @@ -0,0 +1,175 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LiteNetLib.Utils +{ + public static class FastBitConverter + { +#if (LITENETLIB_UNSAFE || LITENETLIB_UNSAFELIB || NETCOREAPP3_1 || NET5_0 || NETCOREAPP3_0_OR_GREATER) && !BIGENDIAN +#if LITENETLIB_UNSAFE + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void GetBytes(byte[] bytes, int startIndex, T value) where T : unmanaged + { + int size = sizeof(T); + if (bytes.Length < startIndex + size) + ThrowIndexOutOfRangeException(); +#if LITENETLIB_UNSAFELIB || NETCOREAPP3_1 || NET5_0 || NETCOREAPP3_0_OR_GREATER + Unsafe.As(ref bytes[startIndex]) = value; +#else + fixed (byte* ptr = &bytes[startIndex]) + { +#if UNITY_ANDROID + // On some android systems, assigning *(T*)ptr throws a NRE if + // the ptr isn't aligned (i.e. if Position is 1,2,3,5, etc.). + // Here we have to use memcpy. + // + // => we can't get a pointer of a struct in C# without + // marshalling allocations + // => instead, we stack allocate an array of type T and use that + // => stackalloc avoids GC and is very fast. it only works for + // value types, but all blittable types are anyway. + T* valueBuffer = stackalloc T[1] { value }; + UnsafeUtility.MemCpy(ptr, valueBuffer, size); +#else + *(T*)ptr = value; +#endif + } +#endif + } +#else + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void GetBytes(byte[] bytes, int startIndex, T value) where T : unmanaged + { + if (bytes.Length < startIndex + Unsafe.SizeOf()) + ThrowIndexOutOfRangeException(); + Unsafe.As(ref bytes[startIndex]) = value; + } +#endif + + private static void ThrowIndexOutOfRangeException() => throw new IndexOutOfRangeException(); +#else + [StructLayout(LayoutKind.Explicit)] + private struct ConverterHelperDouble + { + [FieldOffset(0)] + public ulong Along; + + [FieldOffset(0)] + public double Adouble; + } + + [StructLayout(LayoutKind.Explicit)] + private struct ConverterHelperFloat + { + [FieldOffset(0)] + public int Aint; + + [FieldOffset(0)] + public float Afloat; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WriteLittleEndian(byte[] buffer, int offset, ulong data) + { +#if BIGENDIAN + buffer[offset + 7] = (byte)(data); + buffer[offset + 6] = (byte)(data >> 8); + buffer[offset + 5] = (byte)(data >> 16); + buffer[offset + 4] = (byte)(data >> 24); + buffer[offset + 3] = (byte)(data >> 32); + buffer[offset + 2] = (byte)(data >> 40); + buffer[offset + 1] = (byte)(data >> 48); + buffer[offset ] = (byte)(data >> 56); +#else + buffer[offset] = (byte)(data); + buffer[offset + 1] = (byte)(data >> 8); + buffer[offset + 2] = (byte)(data >> 16); + buffer[offset + 3] = (byte)(data >> 24); + buffer[offset + 4] = (byte)(data >> 32); + buffer[offset + 5] = (byte)(data >> 40); + buffer[offset + 6] = (byte)(data >> 48); + buffer[offset + 7] = (byte)(data >> 56); +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WriteLittleEndian(byte[] buffer, int offset, int data) + { +#if BIGENDIAN + buffer[offset + 3] = (byte)(data); + buffer[offset + 2] = (byte)(data >> 8); + buffer[offset + 1] = (byte)(data >> 16); + buffer[offset ] = (byte)(data >> 24); +#else + buffer[offset] = (byte)(data); + buffer[offset + 1] = (byte)(data >> 8); + buffer[offset + 2] = (byte)(data >> 16); + buffer[offset + 3] = (byte)(data >> 24); +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteLittleEndian(byte[] buffer, int offset, short data) + { +#if BIGENDIAN + buffer[offset + 1] = (byte)(data); + buffer[offset ] = (byte)(data >> 8); +#else + buffer[offset] = (byte)(data); + buffer[offset + 1] = (byte)(data >> 8); +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void GetBytes(byte[] bytes, int startIndex, double value) + { + ConverterHelperDouble ch = new ConverterHelperDouble { Adouble = value }; + WriteLittleEndian(bytes, startIndex, ch.Along); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void GetBytes(byte[] bytes, int startIndex, float value) + { + ConverterHelperFloat ch = new ConverterHelperFloat { Afloat = value }; + WriteLittleEndian(bytes, startIndex, ch.Aint); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void GetBytes(byte[] bytes, int startIndex, short value) + { + WriteLittleEndian(bytes, startIndex, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void GetBytes(byte[] bytes, int startIndex, ushort value) + { + WriteLittleEndian(bytes, startIndex, (short)value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void GetBytes(byte[] bytes, int startIndex, int value) + { + WriteLittleEndian(bytes, startIndex, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void GetBytes(byte[] bytes, int startIndex, uint value) + { + WriteLittleEndian(bytes, startIndex, (int)value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void GetBytes(byte[] bytes, int startIndex, long value) + { + WriteLittleEndian(bytes, startIndex, (ulong)value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void GetBytes(byte[] bytes, int startIndex, ulong value) + { + WriteLittleEndian(bytes, startIndex, value); + } +#endif + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/FastBitConverter.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/FastBitConverter.cs.meta new file mode 100644 index 0000000..784434c --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/FastBitConverter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 21018f84ccfb8244e99b5ec22ceb91c9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/INetSerializable.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/INetSerializable.cs new file mode 100644 index 0000000..92f14be --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/INetSerializable.cs @@ -0,0 +1,8 @@ +namespace LiteNetLib.Utils +{ + public interface INetSerializable + { + void Serialize(NetDataWriter writer); + void Deserialize(NetDataReader reader); + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/INetSerializable.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/INetSerializable.cs.meta new file mode 100644 index 0000000..f055292 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/INetSerializable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c26b17a74d3fb8b498fc89d253f1742a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataReader.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataReader.cs new file mode 100644 index 0000000..7022265 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataReader.cs @@ -0,0 +1,680 @@ +using System; +using System.Net; +using System.Text; + +namespace LiteNetLib.Utils +{ + public class NetDataReader + { + protected byte[] _data; + protected int _position; + protected int _dataSize; + private int _offset; + + public byte[] RawData => _data; + public int RawDataSize => _dataSize; + public int UserDataOffset => _offset; + public int UserDataSize => _dataSize - _offset; + public bool IsNull => _data == null; + public int Position => _position; + public bool EndOfData => _position == _dataSize; + public int AvailableBytes => _dataSize - _position; + + // Cache encoding instead of creating it with BinaryWriter each time + // 1000 readers before: 1MB GC, 30ms + // 1000 readers after: .8MB GC, 18ms + private static readonly UTF8Encoding _uTF8Encoding = new UTF8Encoding(false, true); + + public void SkipBytes(int count) + { + _position += count; + } + + public void SetPosition(int position) + { + _position = position; + } + + public void SetSource(NetDataWriter dataWriter) + { + _data = dataWriter.Data; + _position = 0; + _offset = 0; + _dataSize = dataWriter.Length; + } + + public void SetSource(byte[] source) + { + _data = source; + _position = 0; + _offset = 0; + _dataSize = source.Length; + } + + public void SetSource(byte[] source, int offset, int maxSize) + { + _data = source; + _position = offset; + _offset = offset; + _dataSize = maxSize; + } + + public NetDataReader() + { + + } + + public NetDataReader(NetDataWriter writer) + { + SetSource(writer); + } + + public NetDataReader(byte[] source) + { + SetSource(source); + } + + public NetDataReader(byte[] source, int offset, int maxSize) + { + SetSource(source, offset, maxSize); + } + + #region GetMethods + public IPEndPoint GetNetEndPoint() + { + string host = GetString(1000); + int port = GetInt(); + return NetUtils.MakeEndPoint(host, port); + } + + public byte GetByte() + { + byte res = _data[_position]; + _position += 1; + return res; + } + + public sbyte GetSByte() + { + var b = (sbyte)_data[_position]; + _position++; + return b; + } + + public bool[] GetBoolArray() + { + ushort size = BitConverter.ToUInt16(_data, _position); + _position += 2; + var arr = new bool[size]; + Buffer.BlockCopy(_data, _position, arr, 0, size); + _position += size; + return arr; + } + + public ushort[] GetUShortArray() + { + ushort size = BitConverter.ToUInt16(_data, _position); + _position += 2; + var arr = new ushort[size]; + Buffer.BlockCopy(_data, _position, arr, 0, size * 2); + _position += size * 2; + return arr; + } + + public short[] GetShortArray() + { + ushort size = BitConverter.ToUInt16(_data, _position); + _position += 2; + var arr = new short[size]; + Buffer.BlockCopy(_data, _position, arr, 0, size * 2); + _position += size * 2; + return arr; + } + + public long[] GetLongArray() + { + ushort size = BitConverter.ToUInt16(_data, _position); + _position += 2; + var arr = new long[size]; + Buffer.BlockCopy(_data, _position, arr, 0, size * 8); + _position += size * 8; + return arr; + } + + public ulong[] GetULongArray() + { + ushort size = BitConverter.ToUInt16(_data, _position); + _position += 2; + var arr = new ulong[size]; + Buffer.BlockCopy(_data, _position, arr, 0, size * 8); + _position += size * 8; + return arr; + } + + public int[] GetIntArray() + { + ushort size = BitConverter.ToUInt16(_data, _position); + _position += 2; + var arr = new int[size]; + Buffer.BlockCopy(_data, _position, arr, 0, size * 4); + _position += size * 4; + return arr; + } + + public uint[] GetUIntArray() + { + ushort size = BitConverter.ToUInt16(_data, _position); + _position += 2; + var arr = new uint[size]; + Buffer.BlockCopy(_data, _position, arr, 0, size * 4); + _position += size * 4; + return arr; + } + + public float[] GetFloatArray() + { + ushort size = BitConverter.ToUInt16(_data, _position); + _position += 2; + var arr = new float[size]; + Buffer.BlockCopy(_data, _position, arr, 0, size * 4); + _position += size * 4; + return arr; + } + + public double[] GetDoubleArray() + { + ushort size = BitConverter.ToUInt16(_data, _position); + _position += 2; + var arr = new double[size]; + Buffer.BlockCopy(_data, _position, arr, 0, size * 8); + _position += size * 8; + return arr; + } + + public string[] GetStringArray() + { + ushort arraySize = GetUShort(); + var arr = new string[arraySize]; + for (int i = 0; i < arraySize; i++) + { + arr[i] = GetString(); + } + return arr; + } + + public string[] GetStringArray(int maxStringLength) + { + ushort arraySize = GetUShort(); + var arr = new string[arraySize]; + for (int i = 0; i < arraySize; i++) + { + arr[i] = GetString(maxStringLength); + } + return arr; + } + + public bool GetBool() + { + bool res = _data[_position] > 0; + _position += 1; + return res; + } + + public char GetChar() + { + return (char)GetUShort(); + } + + public ushort GetUShort() + { + ushort result = BitConverter.ToUInt16(_data, _position); + _position += 2; + return result; + } + + public short GetShort() + { + short result = BitConverter.ToInt16(_data, _position); + _position += 2; + return result; + } + + public long GetLong() + { + long result = BitConverter.ToInt64(_data, _position); + _position += 8; + return result; + } + + public ulong GetULong() + { + ulong result = BitConverter.ToUInt64(_data, _position); + _position += 8; + return result; + } + + public int GetInt() + { + int result = BitConverter.ToInt32(_data, _position); + _position += 4; + return result; + } + + public uint GetUInt() + { + uint result = BitConverter.ToUInt32(_data, _position); + _position += 4; + return result; + } + + public float GetFloat() + { + float result = BitConverter.ToSingle(_data, _position); + _position += 4; + return result; + } + + public double GetDouble() + { + double result = BitConverter.ToDouble(_data, _position); + _position += 8; + return result; + } + + /// + /// Note that "maxLength" only limits the number of characters in a string, not its size in bytes. + /// + /// "string.Empty" if value > "maxLength" + public string GetString(int maxLength) + { + ushort size = GetUShort(); + if (size == 0) + { + return null; + } + + int actualSize = size - 1; + if (actualSize >= NetDataWriter.StringBufferMaxLength) + { + return null; + } + + ArraySegment data = GetBytesSegment(actualSize); + + return (maxLength > 0 && _uTF8Encoding.GetCharCount(data.Array, data.Offset, data.Count) > maxLength) ? + string.Empty : + _uTF8Encoding.GetString(data.Array, data.Offset, data.Count); + } + + public string GetString() + { + ushort size = GetUShort(); + if (size == 0) + { + return null; + } + + int actualSize = size - 1; + if (actualSize >= NetDataWriter.StringBufferMaxLength) + { + return null; + } + + ArraySegment data = GetBytesSegment(actualSize); + + return _uTF8Encoding.GetString(data.Array, data.Offset, data.Count); + } + + public ArraySegment GetBytesSegment(int count) + { + ArraySegment segment = new ArraySegment(_data, _position, count); + _position += count; + return segment; + } + + public ArraySegment GetRemainingBytesSegment() + { + ArraySegment segment = new ArraySegment(_data, _position, AvailableBytes); + _position = _data.Length; + return segment; + } + + public T Get() where T : INetSerializable, new() + { + var obj = new T(); + obj.Deserialize(this); + return obj; + } + + public byte[] GetRemainingBytes() + { + byte[] outgoingData = new byte[AvailableBytes]; + Buffer.BlockCopy(_data, _position, outgoingData, 0, AvailableBytes); + _position = _data.Length; + return outgoingData; + } + + public void GetBytes(byte[] destination, int start, int count) + { + Buffer.BlockCopy(_data, _position, destination, start, count); + _position += count; + } + + public void GetBytes(byte[] destination, int count) + { + Buffer.BlockCopy(_data, _position, destination, 0, count); + _position += count; + } + + public sbyte[] GetSBytesWithLength() + { + int length = GetInt(); + sbyte[] outgoingData = new sbyte[length]; + Buffer.BlockCopy(_data, _position, outgoingData, 0, length); + _position += length; + return outgoingData; + } + + public byte[] GetBytesWithLength() + { + int length = GetInt(); + byte[] outgoingData = new byte[length]; + Buffer.BlockCopy(_data, _position, outgoingData, 0, length); + _position += length; + return outgoingData; + } + #endregion + + #region PeekMethods + + public byte PeekByte() + { + return _data[_position]; + } + + public sbyte PeekSByte() + { + return (sbyte)_data[_position]; + } + + public bool PeekBool() + { + return _data[_position] > 0; + } + + public char PeekChar() + { + return (char)PeekUShort(); + } + + public ushort PeekUShort() + { + return BitConverter.ToUInt16(_data, _position); + } + + public short PeekShort() + { + return BitConverter.ToInt16(_data, _position); + } + + public long PeekLong() + { + return BitConverter.ToInt64(_data, _position); + } + + public ulong PeekULong() + { + return BitConverter.ToUInt64(_data, _position); + } + + public int PeekInt() + { + return BitConverter.ToInt32(_data, _position); + } + + public uint PeekUInt() + { + return BitConverter.ToUInt32(_data, _position); + } + + public float PeekFloat() + { + return BitConverter.ToSingle(_data, _position); + } + + public double PeekDouble() + { + return BitConverter.ToDouble(_data, _position); + } + + public string PeekString(int maxLength) + { + ushort size = PeekUShort(); + if (size == 0) + { + return null; + } + + int actualSize = size - 1; + if (actualSize >= NetDataWriter.StringBufferMaxLength) + { + return null; + } + + return (maxLength > 0 && _uTF8Encoding.GetCharCount(_data, _position + 2, actualSize) > maxLength) ? + string.Empty : + _uTF8Encoding.GetString(_data, _position + 2, actualSize); + } + + public string PeekString() + { + ushort size = PeekUShort(); + if (size == 0) + { + return null; + } + + int actualSize = size - 1; + if (actualSize >= NetDataWriter.StringBufferMaxLength) + { + return null; + } + + return _uTF8Encoding.GetString(_data, _position + 2, actualSize); + } + #endregion + + #region TryGetMethods + public bool TryGetByte(out byte result) + { + if (AvailableBytes >= 1) + { + result = GetByte(); + return true; + } + result = 0; + return false; + } + + public bool TryGetSByte(out sbyte result) + { + if (AvailableBytes >= 1) + { + result = GetSByte(); + return true; + } + result = 0; + return false; + } + + public bool TryGetBool(out bool result) + { + if (AvailableBytes >= 1) + { + result = GetBool(); + return true; + } + result = false; + return false; + } + + public bool TryGetChar(out char result) + { + if (!TryGetUShort(out ushort uShortValue)) + { + result = '\0'; + return false; + } + result = (char)uShortValue; + return true; + } + + public bool TryGetShort(out short result) + { + if (AvailableBytes >= 2) + { + result = GetShort(); + return true; + } + result = 0; + return false; + } + + public bool TryGetUShort(out ushort result) + { + if (AvailableBytes >= 2) + { + result = GetUShort(); + return true; + } + result = 0; + return false; + } + + public bool TryGetInt(out int result) + { + if (AvailableBytes >= 4) + { + result = GetInt(); + return true; + } + result = 0; + return false; + } + + public bool TryGetUInt(out uint result) + { + if (AvailableBytes >= 4) + { + result = GetUInt(); + return true; + } + result = 0; + return false; + } + + public bool TryGetLong(out long result) + { + if (AvailableBytes >= 8) + { + result = GetLong(); + return true; + } + result = 0; + return false; + } + + public bool TryGetULong(out ulong result) + { + if (AvailableBytes >= 8) + { + result = GetULong(); + return true; + } + result = 0; + return false; + } + + public bool TryGetFloat(out float result) + { + if (AvailableBytes >= 4) + { + result = GetFloat(); + return true; + } + result = 0; + return false; + } + + public bool TryGetDouble(out double result) + { + if (AvailableBytes >= 8) + { + result = GetDouble(); + return true; + } + result = 0; + return false; + } + + public bool TryGetString(out string result) + { + if (AvailableBytes >= 2) + { + ushort strSize = PeekUShort(); + if (AvailableBytes >= strSize + 1) + { + result = GetString(); + return true; + } + } + result = null; + return false; + } + + public bool TryGetStringArray(out string[] result) + { + ushort strArrayLength; + if (!TryGetUShort(out strArrayLength)) + { + result = null; + return false; + } + + result = new string[strArrayLength]; + for (int i = 0; i < strArrayLength; i++) + { + if (!TryGetString(out result[i])) + { + result = null; + return false; + } + } + + return true; + } + + public bool TryGetBytesWithLength(out byte[] result) + { + if (AvailableBytes >= 4) + { + var length = PeekInt(); + if (length >= 0 && AvailableBytes >= length + 4) + { + result = GetBytesWithLength(); + return true; + } + } + result = null; + return false; + } + #endregion + + public void Clear() + { + _position = 0; + _dataSize = 0; + _data = null; + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataReader.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataReader.cs.meta new file mode 100644 index 0000000..31be4dd --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2876e12b475627f448ca5a6850748bc3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataWriter.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataWriter.cs new file mode 100644 index 0000000..1c9d724 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataWriter.cs @@ -0,0 +1,379 @@ +using System; +using System.Net; +using System.Runtime.CompilerServices; +using System.Text; + +namespace LiteNetLib.Utils +{ + public class NetDataWriter + { + protected byte[] _data; + protected int _position; + private const int InitialSize = 64; + private readonly bool _autoResize; + + public int Capacity => _data.Length; + public byte[] Data => _data; + public int Length => _position; + + // Cache encoding instead of creating it with BinaryWriter each time + // 1000 readers before: 1MB GC, 30ms + // 1000 readers after: .8MB GC, 18ms + private static readonly UTF8Encoding _uTF8Encoding = new UTF8Encoding(false, true); + public const int StringBufferMaxLength = 1024 * 32; // <- short.MaxValue + 1 + private readonly byte[] _stringBuffer = new byte[StringBufferMaxLength]; + + public NetDataWriter() : this(true, InitialSize) + { + } + + public NetDataWriter(bool autoResize) : this(autoResize, InitialSize) + { + } + + public NetDataWriter(bool autoResize, int initialSize) + { + _data = new byte[initialSize]; + _autoResize = autoResize; + } + + /// + /// Creates NetDataWriter from existing ByteArray + /// + /// Source byte array + /// Copy array to new location or use existing + public static NetDataWriter FromBytes(byte[] bytes, bool copy) + { + if (copy) + { + var netDataWriter = new NetDataWriter(true, bytes.Length); + netDataWriter.Put(bytes); + return netDataWriter; + } + return new NetDataWriter(true, 0) {_data = bytes, _position = bytes.Length}; + } + + /// + /// Creates NetDataWriter from existing ByteArray (always copied data) + /// + /// Source byte array + /// Offset of array + /// Length of array + public static NetDataWriter FromBytes(byte[] bytes, int offset, int length) + { + var netDataWriter = new NetDataWriter(true, bytes.Length); + netDataWriter.Put(bytes, offset, length); + return netDataWriter; + } + + public static NetDataWriter FromString(string value) + { + var netDataWriter = new NetDataWriter(); + netDataWriter.Put(value); + return netDataWriter; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ResizeIfNeed(int newSize) + { + if (_data.Length < newSize) + { + Array.Resize(ref _data, Math.Max(newSize, _data.Length * 2)); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void EnsureFit(int additionalSize) + { + if (_data.Length < _position + additionalSize) + { + Array.Resize(ref _data, Math.Max(_position + additionalSize, _data.Length * 2)); + } + } + + public void Reset(int size) + { + ResizeIfNeed(size); + _position = 0; + } + + public void Reset() + { + _position = 0; + } + + public byte[] CopyData() + { + byte[] resultData = new byte[_position]; + Buffer.BlockCopy(_data, 0, resultData, 0, _position); + return resultData; + } + + /// + /// Sets position of NetDataWriter to rewrite previous values + /// + /// new byte position + /// previous position of data writer + public int SetPosition(int position) + { + int prevPosition = _position; + _position = position; + return prevPosition; + } + + public void Put(float value) + { + if (_autoResize) + ResizeIfNeed(_position + 4); + FastBitConverter.GetBytes(_data, _position, value); + _position += 4; + } + + public void Put(double value) + { + if (_autoResize) + ResizeIfNeed(_position + 8); + FastBitConverter.GetBytes(_data, _position, value); + _position += 8; + } + + public void Put(long value) + { + if (_autoResize) + ResizeIfNeed(_position + 8); + FastBitConverter.GetBytes(_data, _position, value); + _position += 8; + } + + public void Put(ulong value) + { + if (_autoResize) + ResizeIfNeed(_position + 8); + FastBitConverter.GetBytes(_data, _position, value); + _position += 8; + } + + public void Put(int value) + { + if (_autoResize) + ResizeIfNeed(_position + 4); + FastBitConverter.GetBytes(_data, _position, value); + _position += 4; + } + + public void Put(uint value) + { + if (_autoResize) + ResizeIfNeed(_position + 4); + FastBitConverter.GetBytes(_data, _position, value); + _position += 4; + } + + public void Put(char value) + { + Put((ushort)value); + } + + public void Put(ushort value) + { + if (_autoResize) + ResizeIfNeed(_position + 2); + FastBitConverter.GetBytes(_data, _position, value); + _position += 2; + } + + public void Put(short value) + { + if (_autoResize) + ResizeIfNeed(_position + 2); + FastBitConverter.GetBytes(_data, _position, value); + _position += 2; + } + + public void Put(sbyte value) + { + if (_autoResize) + ResizeIfNeed(_position + 1); + _data[_position] = (byte)value; + _position++; + } + + public void Put(byte value) + { + if (_autoResize) + ResizeIfNeed(_position + 1); + _data[_position] = value; + _position++; + } + + public void Put(byte[] data, int offset, int length) + { + if (_autoResize) + ResizeIfNeed(_position + length); + Buffer.BlockCopy(data, offset, _data, _position, length); + _position += length; + } + + public void Put(byte[] data) + { + if (_autoResize) + ResizeIfNeed(_position + data.Length); + Buffer.BlockCopy(data, 0, _data, _position, data.Length); + _position += data.Length; + } + + public void PutSBytesWithLength(sbyte[] data, int offset, int length) + { + if (_autoResize) + ResizeIfNeed(_position + length + 4); + FastBitConverter.GetBytes(_data, _position, length); + Buffer.BlockCopy(data, offset, _data, _position + 4, length); + _position += length + 4; + } + + public void PutSBytesWithLength(sbyte[] data) + { + if (_autoResize) + ResizeIfNeed(_position + data.Length + 4); + FastBitConverter.GetBytes(_data, _position, data.Length); + Buffer.BlockCopy(data, 0, _data, _position + 4, data.Length); + _position += data.Length + 4; + } + + public void PutBytesWithLength(byte[] data, int offset, int length) + { + if (_autoResize) + ResizeIfNeed(_position + length + 4); + FastBitConverter.GetBytes(_data, _position, length); + Buffer.BlockCopy(data, offset, _data, _position + 4, length); + _position += length + 4; + } + + public void PutBytesWithLength(byte[] data) + { + if (_autoResize) + ResizeIfNeed(_position + data.Length + 4); + FastBitConverter.GetBytes(_data, _position, data.Length); + Buffer.BlockCopy(data, 0, _data, _position + 4, data.Length); + _position += data.Length + 4; + } + + public void Put(bool value) + { + Put((byte)(value ? 1 : 0)); + } + + private void PutArray(Array arr, int sz) + { + ushort length = arr == null ? (ushort) 0 : (ushort)arr.Length; + sz *= length; + if (_autoResize) + ResizeIfNeed(_position + sz + 2); + FastBitConverter.GetBytes(_data, _position, length); + if (arr != null) + Buffer.BlockCopy(arr, 0, _data, _position + 2, sz); + _position += sz + 2; + } + + public void PutArray(float[] value) + { + PutArray(value, 4); + } + + public void PutArray(double[] value) + { + PutArray(value, 8); + } + + public void PutArray(long[] value) + { + PutArray(value, 8); + } + + public void PutArray(ulong[] value) + { + PutArray(value, 8); + } + + public void PutArray(int[] value) + { + PutArray(value, 4); + } + + public void PutArray(uint[] value) + { + PutArray(value, 4); + } + + public void PutArray(ushort[] value) + { + PutArray(value, 2); + } + + public void PutArray(short[] value) + { + PutArray(value, 2); + } + + public void PutArray(bool[] value) + { + PutArray(value, 1); + } + + public void PutArray(string[] value) + { + ushort strArrayLength = value == null ? (ushort)0 : (ushort)value.Length; + Put(strArrayLength); + for (int i = 0; i < strArrayLength; i++) + Put(value[i]); + } + + public void PutArray(string[] value, int strMaxLength) + { + ushort strArrayLength = value == null ? (ushort)0 : (ushort)value.Length; + Put(strArrayLength); + for (int i = 0; i < strArrayLength; i++) + Put(value[i], strMaxLength); + } + + public void Put(IPEndPoint endPoint) + { + Put(endPoint.Address.ToString()); + Put(endPoint.Port); + } + + public void Put(string value) + { + Put(value, 0); + } + + /// + /// Note that "maxLength" only limits the number of characters in a string, not its size in bytes. + /// + public void Put(string value, int maxLength) + { + if (value == null) + { + Put((ushort)0); + return; + } + + int length = maxLength > 0 && value.Length > maxLength ? maxLength : value.Length; + int size = _uTF8Encoding.GetBytes(value, 0, length, _stringBuffer, 0); + + if (size >= StringBufferMaxLength) + { + Put((ushort)0); + return; + } + + Put(checked((ushort)(size + 1))); + Put(_stringBuffer, 0, size); + } + + public void Put(T obj) where T : INetSerializable + { + obj.Serialize(this); + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataWriter.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataWriter.cs.meta new file mode 100644 index 0000000..448549c --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataWriter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bb3b55fb59ddd9044b1fd56b8543053b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetPacketProcessor.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetPacketProcessor.cs new file mode 100644 index 0000000..007d520 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetPacketProcessor.cs @@ -0,0 +1,296 @@ +using System; +using System.Collections.Generic; + +namespace LiteNetLib.Utils +{ + public class NetPacketProcessor + { + private static class HashCache + { + public static readonly ulong Id; + + //FNV-1 64 bit hash + static HashCache() + { + ulong hash = 14695981039346656037UL; //offset + string typeName = typeof(T).ToString(); + for (var i = 0; i < typeName.Length; i++) + { + hash ^= typeName[i]; + hash *= 1099511628211UL; //prime + } + Id = hash; + } + } + + protected delegate void SubscribeDelegate(NetDataReader reader, object userData); + private readonly NetSerializer _netSerializer; + private readonly Dictionary _callbacks = new Dictionary(); + private readonly NetDataWriter _netDataWriter = new NetDataWriter(); + + public NetPacketProcessor() + { + _netSerializer = new NetSerializer(); + } + + public NetPacketProcessor(int maxStringLength) + { + _netSerializer = new NetSerializer(maxStringLength); + } + + protected virtual ulong GetHash() + { + return HashCache.Id; + } + + protected virtual SubscribeDelegate GetCallbackFromData(NetDataReader reader) + { + ulong hash = reader.GetULong(); + if (!_callbacks.TryGetValue(hash, out var action)) + { + throw new ParseException("Undefined packet in NetDataReader"); + } + return action; + } + + protected virtual void WriteHash(NetDataWriter writer) + { + writer.Put(GetHash()); + } + + /// + /// Register nested property type + /// + /// INetSerializable structure + public void RegisterNestedType() where T : struct, INetSerializable + { + _netSerializer.RegisterNestedType(); + } + + /// + /// Register nested property type + /// + /// + /// + public void RegisterNestedType(Action writeDelegate, Func readDelegate) + { + _netSerializer.RegisterNestedType(writeDelegate, readDelegate); + } + + /// + /// Register nested property type + /// + /// INetSerializable class + public void RegisterNestedType(Func constructor) where T : class, INetSerializable + { + _netSerializer.RegisterNestedType(constructor); + } + + /// + /// Reads all available data from NetDataReader and calls OnReceive delegates + /// + /// NetDataReader with packets data + public void ReadAllPackets(NetDataReader reader) + { + while (reader.AvailableBytes > 0) + ReadPacket(reader); + } + + /// + /// Reads all available data from NetDataReader and calls OnReceive delegates + /// + /// NetDataReader with packets data + /// Argument that passed to OnReceivedEvent + /// Malformed packet + public void ReadAllPackets(NetDataReader reader, object userData) + { + while (reader.AvailableBytes > 0) + ReadPacket(reader, userData); + } + + /// + /// Reads one packet from NetDataReader and calls OnReceive delegate + /// + /// NetDataReader with packet + /// Malformed packet + public void ReadPacket(NetDataReader reader) + { + ReadPacket(reader, null); + } + + public void Send(NetPeer peer, T packet, DeliveryMethod options) where T : class, new() + { + _netDataWriter.Reset(); + Write(_netDataWriter, packet); + peer.Send(_netDataWriter, options); + } + + public void SendNetSerializable(NetPeer peer, ref T packet, DeliveryMethod options) where T : INetSerializable + { + _netDataWriter.Reset(); + WriteNetSerializable(_netDataWriter, ref packet); + peer.Send(_netDataWriter, options); + } + + public void Send(NetManager manager, T packet, DeliveryMethod options) where T : class, new() + { + _netDataWriter.Reset(); + Write(_netDataWriter, packet); + manager.SendToAll(_netDataWriter, options); + } + + public void SendNetSerializable(NetManager manager, ref T packet, DeliveryMethod options) where T : INetSerializable + { + _netDataWriter.Reset(); + WriteNetSerializable(_netDataWriter, ref packet); + manager.SendToAll(_netDataWriter, options); + } + + public void Write(NetDataWriter writer, T packet) where T : class, new() + { + WriteHash(writer); + _netSerializer.Serialize(writer, packet); + } + + public void WriteNetSerializable(NetDataWriter writer, ref T packet) where T : INetSerializable + { + WriteHash(writer); + packet.Serialize(writer); + } + + /// + /// Reads one packet from NetDataReader and calls OnReceive delegate + /// + /// NetDataReader with packet + /// Argument that passed to OnReceivedEvent + /// Malformed packet + public void ReadPacket(NetDataReader reader, object userData) + { + GetCallbackFromData(reader)(reader, userData); + } + + /// + /// Register and subscribe to packet receive event + /// + /// event that will be called when packet deserialized with ReadPacket method + /// Method that constructs packet instead of slow Activator.CreateInstance + /// 's fields are not supported, or it has no fields + public void Subscribe(Action onReceive, Func packetConstructor) where T : class, new() + { + _netSerializer.Register(); + _callbacks[GetHash()] = (reader, userData) => + { + var reference = packetConstructor(); + _netSerializer.Deserialize(reader, reference); + onReceive(reference); + }; + } + + /// + /// Register and subscribe to packet receive event (with userData) + /// + /// event that will be called when packet deserialized with ReadPacket method + /// Method that constructs packet instead of slow Activator.CreateInstance + /// 's fields are not supported, or it has no fields + public void Subscribe(Action onReceive, Func packetConstructor) where T : class, new() + { + _netSerializer.Register(); + _callbacks[GetHash()] = (reader, userData) => + { + var reference = packetConstructor(); + _netSerializer.Deserialize(reader, reference); + onReceive(reference, (TUserData)userData); + }; + } + + /// + /// Register and subscribe to packet receive event + /// This method will overwrite last received packet class on receive (less garbage) + /// + /// event that will be called when packet deserialized with ReadPacket method + /// 's fields are not supported, or it has no fields + public void SubscribeReusable(Action onReceive) where T : class, new() + { + _netSerializer.Register(); + var reference = new T(); + _callbacks[GetHash()] = (reader, userData) => + { + _netSerializer.Deserialize(reader, reference); + onReceive(reference); + }; + } + + /// + /// Register and subscribe to packet receive event + /// This method will overwrite last received packet class on receive (less garbage) + /// + /// event that will be called when packet deserialized with ReadPacket method + /// 's fields are not supported, or it has no fields + public void SubscribeReusable(Action onReceive) where T : class, new() + { + _netSerializer.Register(); + var reference = new T(); + _callbacks[GetHash()] = (reader, userData) => + { + _netSerializer.Deserialize(reader, reference); + onReceive(reference, (TUserData)userData); + }; + } + + public void SubscribeNetSerializable( + Action onReceive, + Func packetConstructor) where T : INetSerializable + { + _callbacks[GetHash()] = (reader, userData) => + { + var pkt = packetConstructor(); + pkt.Deserialize(reader); + onReceive(pkt, (TUserData)userData); + }; + } + + public void SubscribeNetSerializable( + Action onReceive, + Func packetConstructor) where T : INetSerializable + { + _callbacks[GetHash()] = (reader, userData) => + { + var pkt = packetConstructor(); + pkt.Deserialize(reader); + onReceive(pkt); + }; + } + + public void SubscribeNetSerializable( + Action onReceive) where T : INetSerializable, new() + { + var reference = new T(); + _callbacks[GetHash()] = (reader, userData) => + { + reference.Deserialize(reader); + onReceive(reference, (TUserData)userData); + }; + } + + public void SubscribeNetSerializable( + Action onReceive) where T : INetSerializable, new() + { + var reference = new T(); + _callbacks[GetHash()] = (reader, userData) => + { + reference.Deserialize(reader); + onReceive(reference); + }; + } + + /// + /// Remove any subscriptions by type + /// + /// Packet type + /// true if remove is success + public bool RemoveSubscription() + { + return _callbacks.Remove(GetHash()); + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetPacketProcessor.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetPacketProcessor.cs.meta new file mode 100644 index 0000000..e076011 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetPacketProcessor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f0d5a653362556943a36db010a601057 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetSerializer.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetSerializer.cs new file mode 100644 index 0000000..63f6cd6 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetSerializer.cs @@ -0,0 +1,738 @@ +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Net; +using System.Runtime.Serialization; + +namespace LiteNetLib.Utils +{ + public class InvalidTypeException : ArgumentException + { + public InvalidTypeException(string message) : base(message) { } + } + + public class ParseException : Exception + { + public ParseException(string message) : base(message) { } + } + + public class NetSerializer + { + private enum CallType + { + Basic, + Array, + List + } + + private abstract class FastCall + { + public CallType Type; + public virtual void Init(MethodInfo getMethod, MethodInfo setMethod, CallType type) { Type = type; } + public abstract void Read(T inf, NetDataReader r); + public abstract void Write(T inf, NetDataWriter w); + public abstract void ReadArray(T inf, NetDataReader r); + public abstract void WriteArray(T inf, NetDataWriter w); + public abstract void ReadList(T inf, NetDataReader r); + public abstract void WriteList(T inf, NetDataWriter w); + } + + private abstract class FastCallSpecific : FastCall + { + protected Func Getter; + protected Action Setter; + protected Func GetterArr; + protected Action SetterArr; + protected Func> GetterList; + protected Action> SetterList; + + public override void ReadArray(TClass inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: " + typeof(TProperty) + "[]"); } + public override void WriteArray(TClass inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: " + typeof(TProperty) + "[]"); } + public override void ReadList(TClass inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: List<" + typeof(TProperty) + ">"); } + public override void WriteList(TClass inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: List<" + typeof(TProperty) + ">"); } + + protected TProperty[] ReadArrayHelper(TClass inf, NetDataReader r) + { + ushort count = r.GetUShort(); + var arr = GetterArr(inf); + arr = arr == null || arr.Length != count ? new TProperty[count] : arr; + SetterArr(inf, arr); + return arr; + } + + protected TProperty[] WriteArrayHelper(TClass inf, NetDataWriter w) + { + var arr = GetterArr(inf); + w.Put((ushort)arr.Length); + return arr; + } + + protected List ReadListHelper(TClass inf, NetDataReader r, out int len) + { + len = r.GetUShort(); + var list = GetterList(inf); + if (list == null) + { + list = new List(len); + SetterList(inf, list); + } + return list; + } + + protected List WriteListHelper(TClass inf, NetDataWriter w, out int len) + { + var list = GetterList(inf); + if (list == null) + { + len = 0; + w.Put(0); + return null; + } + len = list.Count; + w.Put((ushort)len); + return list; + } + + public override void Init(MethodInfo getMethod, MethodInfo setMethod, CallType type) + { + base.Init(getMethod, setMethod, type); + switch (type) + { + case CallType.Array: + GetterArr = (Func)Delegate.CreateDelegate(typeof(Func), getMethod); + SetterArr = (Action)Delegate.CreateDelegate(typeof(Action), setMethod); + break; + case CallType.List: + GetterList = (Func>)Delegate.CreateDelegate(typeof(Func>), getMethod); + SetterList = (Action>)Delegate.CreateDelegate(typeof(Action>), setMethod); + break; + default: + Getter = (Func)Delegate.CreateDelegate(typeof(Func), getMethod); + Setter = (Action)Delegate.CreateDelegate(typeof(Action), setMethod); + break; + } + } + } + + private abstract class FastCallSpecificAuto : FastCallSpecific + { + protected abstract void ElementRead(NetDataReader r, out TProperty prop); + protected abstract void ElementWrite(NetDataWriter w, ref TProperty prop); + + public override void Read(TClass inf, NetDataReader r) + { + ElementRead(r, out var elem); + Setter(inf, elem); + } + + public override void Write(TClass inf, NetDataWriter w) + { + var elem = Getter(inf); + ElementWrite(w, ref elem); + } + + public override void ReadArray(TClass inf, NetDataReader r) + { + var arr = ReadArrayHelper(inf, r); + for (int i = 0; i < arr.Length; i++) + ElementRead(r, out arr[i]); + } + + public override void WriteArray(TClass inf, NetDataWriter w) + { + var arr = WriteArrayHelper(inf, w); + for (int i = 0; i < arr.Length; i++) + ElementWrite(w, ref arr[i]); + } + } + + private sealed class FastCallStatic : FastCallSpecific + { + private readonly Action _writer; + private readonly Func _reader; + + public FastCallStatic(Action write, Func read) + { + _writer = write; + _reader = read; + } + + public override void Read(TClass inf, NetDataReader r) { Setter(inf, _reader(r)); } + public override void Write(TClass inf, NetDataWriter w) { _writer(w, Getter(inf)); } + + public override void ReadList(TClass inf, NetDataReader r) + { + var list = ReadListHelper(inf, r, out int len); + int listCount = list.Count; + for (int i = 0; i < len; i++) + { + if (i < listCount) + list[i] = _reader(r); + else + list.Add(_reader(r)); + } + if (len < listCount) + list.RemoveRange(len, listCount - len); + } + + public override void WriteList(TClass inf, NetDataWriter w) + { + var list = WriteListHelper(inf, w, out int len); + for (int i = 0; i < len; i++) + _writer(w, list[i]); + } + + public override void ReadArray(TClass inf, NetDataReader r) + { + var arr = ReadArrayHelper(inf, r); + int len = arr.Length; + for (int i = 0; i < len; i++) + arr[i] = _reader(r); + } + + public override void WriteArray(TClass inf, NetDataWriter w) + { + var arr = WriteArrayHelper(inf, w); + int len = arr.Length; + for (int i = 0; i < len; i++) + _writer(w, arr[i]); + } + } + + private sealed class FastCallStruct : FastCallSpecific where TProperty : struct, INetSerializable + { + private TProperty _p; + + public override void Read(TClass inf, NetDataReader r) + { + _p.Deserialize(r); + Setter(inf, _p); + } + + public override void Write(TClass inf, NetDataWriter w) + { + _p = Getter(inf); + _p.Serialize(w); + } + + public override void ReadList(TClass inf, NetDataReader r) + { + var list = ReadListHelper(inf, r, out int len); + int listCount = list.Count; + for (int i = 0; i < len; i++) + { + var itm = default(TProperty); + itm.Deserialize(r); + if(i < listCount) + list[i] = itm; + else + list.Add(itm); + } + if (len < listCount) + list.RemoveRange(len, listCount - len); + } + + public override void WriteList(TClass inf, NetDataWriter w) + { + var list = WriteListHelper(inf, w, out int len); + for (int i = 0; i < len; i++) + list[i].Serialize(w); + } + + public override void ReadArray(TClass inf, NetDataReader r) + { + var arr = ReadArrayHelper(inf, r); + int len = arr.Length; + for (int i = 0; i < len; i++) + arr[i].Deserialize(r); + } + + public override void WriteArray(TClass inf, NetDataWriter w) + { + var arr = WriteArrayHelper(inf, w); + int len = arr.Length; + for (int i = 0; i < len; i++) + arr[i].Serialize(w); + } + } + + private sealed class FastCallClass : FastCallSpecific where TProperty : class, INetSerializable + { + private readonly Func _constructor; + public FastCallClass(Func constructor) { _constructor = constructor; } + + public override void Read(TClass inf, NetDataReader r) + { + var p = _constructor(); + p.Deserialize(r); + Setter(inf, p); + } + + public override void Write(TClass inf, NetDataWriter w) + { + var p = Getter(inf); + p?.Serialize(w); + } + + public override void ReadList(TClass inf, NetDataReader r) + { + var list = ReadListHelper(inf, r, out int len); + int listCount = list.Count; + for (int i = 0; i < len; i++) + { + if (i < listCount) + { + list[i].Deserialize(r); + } + else + { + var itm = _constructor(); + itm.Deserialize(r); + list.Add(itm); + } + } + if (len < listCount) + list.RemoveRange(len, listCount - len); + } + + public override void WriteList(TClass inf, NetDataWriter w) + { + var list = WriteListHelper(inf, w, out int len); + for (int i = 0; i < len; i++) + list[i].Serialize(w); + } + + public override void ReadArray(TClass inf, NetDataReader r) + { + var arr = ReadArrayHelper(inf, r); + int len = arr.Length; + for (int i = 0; i < len; i++) + { + arr[i] = _constructor(); + arr[i].Deserialize(r); + } + } + + public override void WriteArray(TClass inf, NetDataWriter w) + { + var arr = WriteArrayHelper(inf, w); + int len = arr.Length; + for (int i = 0; i < len; i++) + arr[i].Serialize(w); + } + } + + private class IntSerializer : FastCallSpecific + { + public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetInt()); } + public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } + public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetIntArray()); } + public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } + } + + private class UIntSerializer : FastCallSpecific + { + public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetUInt()); } + public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } + public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetUIntArray()); } + public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } + } + + private class ShortSerializer : FastCallSpecific + { + public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetShort()); } + public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } + public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetShortArray()); } + public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } + } + + private class UShortSerializer : FastCallSpecific + { + public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetUShort()); } + public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } + public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetUShortArray()); } + public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } + } + + private class LongSerializer : FastCallSpecific + { + public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetLong()); } + public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } + public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetLongArray()); } + public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } + } + + private class ULongSerializer : FastCallSpecific + { + public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetULong()); } + public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } + public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetULongArray()); } + public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } + } + + private class ByteSerializer : FastCallSpecific + { + public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetByte()); } + public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } + public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetBytesWithLength()); } + public override void WriteArray(T inf, NetDataWriter w) { w.PutBytesWithLength(GetterArr(inf)); } + } + + private class SByteSerializer : FastCallSpecific + { + public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetSByte()); } + public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } + public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetSBytesWithLength()); } + public override void WriteArray(T inf, NetDataWriter w) { w.PutSBytesWithLength(GetterArr(inf)); } + } + + private class FloatSerializer : FastCallSpecific + { + public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetFloat()); } + public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } + public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetFloatArray()); } + public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } + } + + private class DoubleSerializer : FastCallSpecific + { + public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetDouble()); } + public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } + public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetDoubleArray()); } + public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } + } + + private class BoolSerializer : FastCallSpecific + { + public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetBool()); } + public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } + public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetBoolArray()); } + public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } + } + + private class CharSerializer : FastCallSpecificAuto + { + protected override void ElementWrite(NetDataWriter w, ref char prop) { w.Put(prop); } + protected override void ElementRead(NetDataReader r, out char prop) { prop = r.GetChar(); } + } + + private class IPEndPointSerializer : FastCallSpecificAuto + { + protected override void ElementWrite(NetDataWriter w, ref IPEndPoint prop) { w.Put(prop); } + protected override void ElementRead(NetDataReader r, out IPEndPoint prop) { prop = r.GetNetEndPoint(); } + } + + private class StringSerializer : FastCallSpecific + { + private readonly int _maxLength; + public StringSerializer(int maxLength) { _maxLength = maxLength > 0 ? maxLength : short.MaxValue; } + public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetString(_maxLength)); } + public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf), _maxLength); } + public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetStringArray(_maxLength)); } + public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf), _maxLength); } + } + + private class EnumByteSerializer : FastCall + { + protected readonly PropertyInfo Property; + protected readonly Type PropertyType; + public EnumByteSerializer(PropertyInfo property, Type propertyType) + { + Property = property; + PropertyType = propertyType; + } + public override void Read(T inf, NetDataReader r) { Property.SetValue(inf, Enum.ToObject(PropertyType, r.GetByte()), null); } + public override void Write(T inf, NetDataWriter w) { w.Put((byte)Property.GetValue(inf, null)); } + public override void ReadArray(T inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: Enum[]"); } + public override void WriteArray(T inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: Enum[]"); } + public override void ReadList(T inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: List"); } + public override void WriteList(T inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: List"); } + } + + private class EnumIntSerializer : EnumByteSerializer + { + public EnumIntSerializer(PropertyInfo property, Type propertyType) : base(property, propertyType) { } + public override void Read(T inf, NetDataReader r) { Property.SetValue(inf, Enum.ToObject(PropertyType, r.GetInt()), null); } + public override void Write(T inf, NetDataWriter w) { w.Put((int)Property.GetValue(inf, null)); } + } + + private sealed class ClassInfo + { + public static ClassInfo Instance; + private readonly FastCall[] _serializers; + private readonly int _membersCount; + + public ClassInfo(List> serializers) + { + _membersCount = serializers.Count; + _serializers = serializers.ToArray(); + } + + public void Write(T obj, NetDataWriter writer) + { + for (int i = 0; i < _membersCount; i++) + { + var s = _serializers[i]; + if (s.Type == CallType.Basic) + s.Write(obj, writer); + else if (s.Type == CallType.Array) + s.WriteArray(obj, writer); + else + s.WriteList(obj, writer); + } + } + + public void Read(T obj, NetDataReader reader) + { + for (int i = 0; i < _membersCount; i++) + { + var s = _serializers[i]; + if (s.Type == CallType.Basic) + s.Read(obj, reader); + else if(s.Type == CallType.Array) + s.ReadArray(obj, reader); + else + s.ReadList(obj, reader); + } + } + } + + private abstract class CustomType + { + public abstract FastCall Get(); + } + + private sealed class CustomTypeStruct : CustomType where TProperty : struct, INetSerializable + { + public override FastCall Get() { return new FastCallStruct(); } + } + + private sealed class CustomTypeClass : CustomType where TProperty : class, INetSerializable + { + private readonly Func _constructor; + public CustomTypeClass(Func constructor) { _constructor = constructor; } + public override FastCall Get() { return new FastCallClass(_constructor); } + } + + private sealed class CustomTypeStatic : CustomType + { + private readonly Action _writer; + private readonly Func _reader; + public CustomTypeStatic(Action writer, Func reader) + { + _writer = writer; + _reader = reader; + } + public override FastCall Get() { return new FastCallStatic(_writer, _reader); } + } + + /// + /// Register custom property type + /// + /// INetSerializable structure + public void RegisterNestedType() where T : struct, INetSerializable + { + _registeredTypes.Add(typeof(T), new CustomTypeStruct()); + } + + /// + /// Register custom property type + /// + /// INetSerializable class + public void RegisterNestedType(Func constructor) where T : class, INetSerializable + { + _registeredTypes.Add(typeof(T), new CustomTypeClass(constructor)); + } + + /// + /// Register custom property type + /// + /// Any packet + /// custom type writer + /// custom type reader + public void RegisterNestedType(Action writer, Func reader) + { + _registeredTypes.Add(typeof(T), new CustomTypeStatic(writer, reader)); + } + + private NetDataWriter _writer; + private readonly int _maxStringLength; + private readonly Dictionary _registeredTypes = new Dictionary(); + + public NetSerializer() : this(0) + { + } + + public NetSerializer(int maxStringLength) + { + _maxStringLength = maxStringLength; + } + + private ClassInfo RegisterInternal() + { + if (ClassInfo.Instance != null) + return ClassInfo.Instance; + + Type t = typeof(T); + var props = t.GetProperties( + BindingFlags.Instance | + BindingFlags.Public | + BindingFlags.GetProperty | + BindingFlags.SetProperty); + var serializers = new List>(); + for (int i = 0; i < props.Length; i++) + { + var property = props[i]; + var propertyType = property.PropertyType; + + var elementType = propertyType.IsArray ? propertyType.GetElementType() : propertyType; + var callType = propertyType.IsArray ? CallType.Array : CallType.Basic; + + if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(List<>)) + { + elementType = propertyType.GetGenericArguments()[0]; + callType = CallType.List; + } + + if (Attribute.IsDefined(property, typeof(IgnoreDataMemberAttribute))) + continue; + + var getMethod = property.GetGetMethod(); + var setMethod = property.GetSetMethod(); + if (getMethod == null || setMethod == null) + continue; + + FastCall serialzer = null; + if (propertyType.IsEnum) + { + var underlyingType = Enum.GetUnderlyingType(propertyType); + if (underlyingType == typeof(byte)) + serialzer = new EnumByteSerializer(property, propertyType); + else if (underlyingType == typeof(int)) + serialzer = new EnumIntSerializer(property, propertyType); + else + throw new InvalidTypeException("Not supported enum underlying type: " + underlyingType.Name); + } + else if (elementType == typeof(string)) + serialzer = new StringSerializer(_maxStringLength); + else if (elementType == typeof(bool)) + serialzer = new BoolSerializer(); + else if (elementType == typeof(byte)) + serialzer = new ByteSerializer(); + else if (elementType == typeof(sbyte)) + serialzer = new SByteSerializer(); + else if (elementType == typeof(short)) + serialzer = new ShortSerializer(); + else if (elementType == typeof(ushort)) + serialzer = new UShortSerializer(); + else if (elementType == typeof(int)) + serialzer = new IntSerializer(); + else if (elementType == typeof(uint)) + serialzer = new UIntSerializer(); + else if (elementType == typeof(long)) + serialzer = new LongSerializer(); + else if (elementType == typeof(ulong)) + serialzer = new ULongSerializer(); + else if (elementType == typeof(float)) + serialzer = new FloatSerializer(); + else if (elementType == typeof(double)) + serialzer = new DoubleSerializer(); + else if (elementType == typeof(char)) + serialzer = new CharSerializer(); + else if (elementType == typeof(IPEndPoint)) + serialzer = new IPEndPointSerializer(); + else + { + _registeredTypes.TryGetValue(elementType, out var customType); + if (customType != null) + serialzer = customType.Get(); + } + + if (serialzer != null) + { + serialzer.Init(getMethod, setMethod, callType); + serializers.Add(serialzer); + } + else + { + throw new InvalidTypeException("Unknown property type: " + propertyType.FullName); + } + } + ClassInfo.Instance = new ClassInfo(serializers); + return ClassInfo.Instance; + } + + /// 's fields are not supported, or it has no fields + public void Register() + { + RegisterInternal(); + } + + /// + /// Reads packet with known type + /// + /// NetDataReader with packet + /// Returns packet if packet in reader is matched type + /// 's fields are not supported, or it has no fields + public T Deserialize(NetDataReader reader) where T : class, new() + { + var info = RegisterInternal(); + var result = new T(); + try + { + info.Read(result, reader); + } + catch + { + return null; + } + return result; + } + + /// + /// Reads packet with known type (non alloc variant) + /// + /// NetDataReader with packet + /// Deserialization target + /// Returns true if packet in reader is matched type + /// 's fields are not supported, or it has no fields + public bool Deserialize(NetDataReader reader, T target) where T : class, new() + { + var info = RegisterInternal(); + try + { + info.Read(target, reader); + } + catch + { + return false; + } + return true; + } + + /// + /// Serialize object to NetDataWriter (fast) + /// + /// Serialization target NetDataWriter + /// Object to serialize + /// 's fields are not supported, or it has no fields + public void Serialize(NetDataWriter writer, T obj) where T : class, new() + { + RegisterInternal().Write(obj, writer); + } + + /// + /// Serialize object to byte array + /// + /// Object to serialize + /// byte array with serialized data + public byte[] Serialize(T obj) where T : class, new() + { + if (_writer == null) + _writer = new NetDataWriter(); + _writer.Reset(); + Serialize(_writer, obj); + return _writer.CopyData(); + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetSerializer.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetSerializer.cs.meta new file mode 100644 index 0000000..87dacc8 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetSerializer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 503136cdfd845ea439d6eb1e7fcfa924 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NtpPacket.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NtpPacket.cs new file mode 100644 index 0000000..1ba5210 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NtpPacket.cs @@ -0,0 +1,423 @@ +using System; + +namespace LiteNetLib.Utils +{ + /// + /// Represents RFC4330 SNTP packet used for communication to and from a network time server. + /// + /// + /// + /// Most applications should just use the property. + /// + /// + /// The same data structure represents both request and reply packets. + /// Request and reply differ in which properties are set and to what values. + /// + /// + /// The only real property is . + /// All other properties read from and write to the underlying byte array + /// with the exception of , + /// which is not part of the packet on network and it is instead set locally after receiving the packet. + /// + /// + /// Copied from GuerrillaNtp project + /// with permission from Robert Vazan (@robertvazan) under MIT license, see https://github.com/RevenantX/LiteNetLib/pull/236 + /// + /// + public class NtpPacket + { + private static readonly DateTime Epoch = new DateTime(1900, 1, 1); + + /// + /// Gets RFC4330-encoded SNTP packet. + /// + /// + /// Byte array containing RFC4330-encoded SNTP packet. It is at least 48 bytes long. + /// + /// + /// This is the only real property. All other properties except + /// read from or write to this byte array. + /// + public byte[] Bytes { get; } + + /// + /// Gets the leap second indicator. + /// + /// + /// Leap second warning, if any. Special value + /// indicates unsynchronized server clock. + /// Default is . + /// + /// + /// Only servers fill in this property. Clients can consult this property for possible leap second warning. + /// + public NtpLeapIndicator LeapIndicator => (NtpLeapIndicator)((Bytes[0] & 0xC0) >> 6); + + /// + /// Gets or sets protocol version number. + /// + /// + /// SNTP protocol version. Default is 4, which is the latest version at the time of this writing. + /// + /// + /// In request packets, clients should leave this property at default value 4. + /// Servers usually reply with the same protocol version. + /// + public int VersionNumber + { + get => (Bytes[0] & 0x38) >> 3; + private set => Bytes[0] = (byte)((Bytes[0] & ~0x38) | value << 3); + } + + /// + /// Gets or sets SNTP packet mode, i.e. whether this is client or server packet. + /// + /// + /// SNTP packet mode. Default is in newly created packets. + /// Server reply should have this property set to . + /// + public NtpMode Mode + { + get => (NtpMode)(Bytes[0] & 0x07); + private set => Bytes[0] = (byte)((Bytes[0] & ~0x07) | (int)value); + } + + /// + /// Gets server's distance from the reference clock. + /// + /// + /// + /// Distance from the reference clock. This property is set only in server reply packets. + /// Servers connected directly to reference clock hardware set this property to 1. + /// Statum number is incremented by 1 on every hop down the NTP server hierarchy. + /// + /// + /// Special value 0 indicates that this packet is a Kiss-o'-Death message + /// with kiss code stored in . + /// + /// + public int Stratum => Bytes[1]; + + /// + /// Gets server's preferred polling interval. + /// + /// + /// Polling interval in log2 seconds, e.g. 4 stands for 16s and 17 means 131,072s. + /// + public int Poll => Bytes[2]; + + /// + /// Gets the precision of server clock. + /// + /// + /// Clock precision in log2 seconds, e.g. -20 for microsecond precision. + /// + public int Precision => (sbyte)Bytes[3]; + + /// + /// Gets the total round-trip delay from the server to the reference clock. + /// + /// + /// Round-trip delay to the reference clock. Normally a positive value smaller than one second. + /// + public TimeSpan RootDelay => GetTimeSpan32(4); + + /// + /// Gets the estimated error in time reported by the server. + /// + /// + /// Estimated error in time reported by the server. Normally a positive value smaller than one second. + /// + public TimeSpan RootDispersion => GetTimeSpan32(8); + + /// + /// Gets the ID of the time source used by the server or Kiss-o'-Death code sent by the server. + /// + /// + /// + /// ID of server's time source or Kiss-o'-Death code. + /// Purpose of this property depends on value of property. + /// + /// + /// Stratum 1 servers write here one of several special values that describe the kind of hardware clock they use. + /// + /// + /// Stratum 2 and lower servers set this property to IPv4 address of their upstream server. + /// If upstream server has IPv6 address, the address is hashed, because it doesn't fit in this property. + /// + /// + /// When server sets to special value 0, + /// this property contains so called kiss code that instructs the client to stop querying the server. + /// + /// + public uint ReferenceId => GetUInt32BE(12); + + /// + /// Gets or sets the time when the server clock was last set or corrected. + /// + /// + /// Time when the server clock was last set or corrected or null when not specified. + /// + /// + /// This Property is usually set only by servers. It usually lags server's current time by several minutes, + /// so don't use this property for time synchronization. + /// + public DateTime? ReferenceTimestamp => GetDateTime64(16); + + /// + /// Gets or sets the time when the client sent its request. + /// + /// + /// This property is null in request packets. + /// In reply packets, it is the time when the client sent its request. + /// Servers copy this value from + /// that they find in received request packet. + /// + /// + /// + public DateTime? OriginTimestamp => GetDateTime64(24); + + /// + /// Gets or sets the time when the request was received by the server. + /// + /// + /// This property is null in request packets. + /// In reply packets, it is the time when the server received client request. + /// + /// + /// + public DateTime? ReceiveTimestamp => GetDateTime64(32); + + /// + /// Gets or sets the time when the packet was sent. + /// + /// + /// Time when the packet was sent. It should never be null. + /// Default value is . + /// + /// + /// This property must be set by both clients and servers. + /// + /// + /// + public DateTime? TransmitTimestamp { get { return GetDateTime64(40); } private set { SetDateTime64(40, value); } } + + /// + /// Gets or sets the time of reception of response SNTP packet on the client. + /// + /// + /// Time of reception of response SNTP packet on the client. It is null in request packets. + /// + /// + /// This property is not part of the protocol and has to be set when reply packet is received. + /// + /// + /// + public DateTime? DestinationTimestamp { get; private set; } + + /// + /// Gets the round-trip time to the server. + /// + /// + /// Time the request spent traveling to the server plus the time the reply spent traveling back. + /// This is calculated from timestamps in the packet as (t1 - t0) + (t3 - t2) + /// where t0 is , + /// t1 is , + /// t2 is , + /// and t3 is . + /// This property throws an exception in request packets. + /// + public TimeSpan RoundTripTime + { + get + { + CheckTimestamps(); + return (ReceiveTimestamp.Value - OriginTimestamp.Value) + (DestinationTimestamp.Value - TransmitTimestamp.Value); + } + } + + /// + /// Gets the offset that should be added to local time to synchronize it with server time. + /// + /// + /// Time difference between server and client. It should be added to local time to get server time. + /// It is calculated from timestamps in the packet as 0.5 * ((t1 - t0) - (t3 - t2)) + /// where t0 is , + /// t1 is , + /// t2 is , + /// and t3 is . + /// This property throws an exception in request packets. + /// + public TimeSpan CorrectionOffset + { + get + { + CheckTimestamps(); + return TimeSpan.FromTicks(((ReceiveTimestamp.Value - OriginTimestamp.Value) - (DestinationTimestamp.Value - TransmitTimestamp.Value)).Ticks / 2); + } + } + + /// + /// Initializes default request packet. + /// + /// + /// Properties and + /// are set appropriately for request packet. Property + /// is set to . + /// + public NtpPacket() : this(new byte[48]) + { + Mode = NtpMode.Client; + VersionNumber = 4; + TransmitTimestamp = DateTime.UtcNow; + } + + /// + /// Initializes packet from received data. + /// + internal NtpPacket(byte[] bytes) + { + if (bytes.Length < 48) + throw new ArgumentException("SNTP reply packet must be at least 48 bytes long.", "bytes"); + Bytes = bytes; + } + + /// + /// Initializes packet from data received from a server. + /// + /// Data received from the server. + /// Utc time of reception of response SNTP packet on the client. + /// + public static NtpPacket FromServerResponse(byte[] bytes, DateTime destinationTimestamp) + { + return new NtpPacket(bytes) { DestinationTimestamp = destinationTimestamp }; + } + + internal void ValidateRequest() + { + if (Mode != NtpMode.Client) + throw new InvalidOperationException("This is not a request SNTP packet."); + if (VersionNumber == 0) + throw new InvalidOperationException("Protocol version of the request is not specified."); + if (TransmitTimestamp == null) + throw new InvalidOperationException("TransmitTimestamp must be set in request packet."); + } + + internal void ValidateReply() + { + if (Mode != NtpMode.Server) + throw new InvalidOperationException("This is not a reply SNTP packet."); + if (VersionNumber == 0) + throw new InvalidOperationException("Protocol version of the reply is not specified."); + if (Stratum == 0) + throw new InvalidOperationException(string.Format("Received Kiss-o'-Death SNTP packet with code 0x{0:x}.", ReferenceId)); + if (LeapIndicator == NtpLeapIndicator.AlarmCondition) + throw new InvalidOperationException("SNTP server has unsynchronized clock."); + CheckTimestamps(); + } + + private void CheckTimestamps() + { + if (OriginTimestamp == null) + throw new InvalidOperationException("Origin timestamp is missing."); + if (ReceiveTimestamp == null) + throw new InvalidOperationException("Receive timestamp is missing."); + if (TransmitTimestamp == null) + throw new InvalidOperationException("Transmit timestamp is missing."); + if (DestinationTimestamp == null) + throw new InvalidOperationException("Destination timestamp is missing."); + } + + private DateTime? GetDateTime64(int offset) + { + var field = GetUInt64BE(offset); + if (field == 0) + return null; + return new DateTime(Epoch.Ticks + Convert.ToInt64(field * (1.0 / (1L << 32) * 10000000.0))); + } + + private void SetDateTime64(int offset, DateTime? value) + { + SetUInt64BE(offset, value == null ? 0 : Convert.ToUInt64((value.Value.Ticks - Epoch.Ticks) * (0.0000001 * (1L << 32)))); + } + + private TimeSpan GetTimeSpan32(int offset) + { + return TimeSpan.FromSeconds(GetInt32BE(offset) / (double)(1 << 16)); + } + + private ulong GetUInt64BE(int offset) + { + return SwapEndianness(BitConverter.ToUInt64(Bytes, offset)); + } + + private void SetUInt64BE(int offset, ulong value) + { + FastBitConverter.GetBytes(Bytes, offset, SwapEndianness(value)); + } + + private int GetInt32BE(int offset) + { + return (int)GetUInt32BE(offset); + } + + private uint GetUInt32BE(int offset) + { + return SwapEndianness(BitConverter.ToUInt32(Bytes, offset)); + } + + private static uint SwapEndianness(uint x) + { + return ((x & 0xff) << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | ((x & 0xff000000) >> 24); + } + + private static ulong SwapEndianness(ulong x) + { + return ((ulong)SwapEndianness((uint)x) << 32) | SwapEndianness((uint)(x >> 32)); + } + } + + /// + /// Represents leap second warning from the server that instructs the client to add or remove leap second. + /// + /// + public enum NtpLeapIndicator + { + /// + /// No leap second warning. No action required. + /// + NoWarning, + + /// + /// Warns the client that the last minute of the current day has 61 seconds. + /// + LastMinuteHas61Seconds, + + /// + /// Warns the client that the last minute of the current day has 59 seconds. + /// + LastMinuteHas59Seconds, + + /// + /// Special value indicating that the server clock is unsynchronized and the returned time is unreliable. + /// + AlarmCondition + } + + /// + /// Describes SNTP packet mode, i.e. client or server. + /// + /// + public enum NtpMode + { + /// + /// Identifies client-to-server SNTP packet. + /// + Client = 3, + + /// + /// Identifies server-to-client SNTP packet. + /// + Server = 4, + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NtpPacket.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NtpPacket.cs.meta new file mode 100644 index 0000000..fdbbe00 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NtpPacket.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: daf31cf4ab8132943b2c8ca301fc919a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NtpRequest.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NtpRequest.cs new file mode 100644 index 0000000..bd7f74f --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NtpRequest.cs @@ -0,0 +1,42 @@ +using System.Net; +using System.Net.Sockets; + +namespace LiteNetLib.Utils +{ + internal sealed class NtpRequest + { + private const int ResendTimer = 1000; + private const int KillTimer = 10000; + public const int DefaultPort = 123; + private readonly IPEndPoint _ntpEndPoint; + private int _resendTime = ResendTimer; + private int _killTime = 0; + + public NtpRequest(IPEndPoint endPoint) + { + _ntpEndPoint = endPoint; + } + + public bool NeedToKill => _killTime >= KillTimer; + + public bool Send(Socket socket, int time) + { + _resendTime += time; + _killTime += time; + if (_resendTime < ResendTimer) + { + return false; + } + var packet = new NtpPacket(); + try + { + int sendCount = socket.SendTo(packet.Bytes, 0, packet.Bytes.Length, SocketFlags.None, _ntpEndPoint); + return sendCount == packet.Bytes.Length; + } + catch + { + return false; + } + } + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NtpRequest.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NtpRequest.cs.meta new file mode 100644 index 0000000..aa80855 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NtpRequest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c3fa3bfbb02dd944e939070e3cf0638c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Tugboat.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Tugboat.cs new file mode 100644 index 0000000..5ef6969 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Tugboat.cs @@ -0,0 +1,505 @@ +using FishNet.Managing; +using FishNet.Managing.Logging; +using FishNet.Managing.Transporting; +using LiteNetLib; +using LiteNetLib.Layers; +using System; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Transporting.Tugboat +{ + [DisallowMultipleComponent] + [AddComponentMenu("FishNet/Transport/Tugboat")] + public class Tugboat : Transport + { + ~Tugboat() + { + Shutdown(); + } + + #region Serialized. + [Header("Channels")] + /// + /// Maximum transmission unit for the unreliable channel. + /// + [Tooltip("Maximum transmission unit for the unreliable channel.")] + [Range(MINIMUM_UDP_MTU, MAXIMUM_UDP_MTU)] + [SerializeField] + private int _unreliableMTU = 1023; + + [Header("Server")] + /// + /// IPv4 address to bind server to. + /// + [Tooltip("IPv4 Address to bind server to.")] + [SerializeField] + private string _ipv4BindAddress; + /// + /// IPv6 address to bind server to. + /// + [Tooltip("IPv6 Address to bind server to.")] + [SerializeField] + private string _ipv6BindAddress; + /// + /// Port to use. + /// + [Tooltip("Port to use.")] + [SerializeField] + private ushort _port = 7770; + /// + /// Maximum number of players which may be connected at once. + /// + [Tooltip("Maximum number of players which may be connected at once.")] + [Range(1, 9999)] + [SerializeField] + private int _maximumClients = 4095; + + + [Header("Client")] + /// + /// Address to connect. + /// + [Tooltip("Address to connect.")] + [SerializeField] + private string _clientAddress = "localhost"; + + [Header("Misc")] + /// + /// How long in seconds until either the server or client socket must go without data before being timed out. Use 0f to disable timing out. + /// + [Tooltip("How long in seconds until either the server or client socket must go without data before being timed out. Use 0f to disable timing out.")] + [Range(0, MAX_TIMEOUT_SECONDS)] + [SerializeField] + private ushort _timeout = 15; + #endregion + + #region Private. + /// + /// PacketLayer to use with LiteNetLib. + /// + private PacketLayerBase _packetLayer; + /// + /// Server socket and handler. + /// + private Server.ServerSocket _server = new Server.ServerSocket(); + /// + /// Client socket and handler. + /// + private Client.ClientSocket _client = new Client.ClientSocket(); + #endregion + + #region Const. + private const ushort MAX_TIMEOUT_SECONDS = 1800; + /// + /// Minimum UDP packet size allowed. + /// + private const int MINIMUM_UDP_MTU = 576; + /// + /// Maximum UDP packet size allowed. + /// + private const int MAXIMUM_UDP_MTU = 1023; + #endregion + + #region Initialization and unity. + public override void Initialize(NetworkManager networkManager, int transportIndex) + { + base.Initialize(networkManager, transportIndex); + } + + protected void OnDestroy() + { + Shutdown(); + } + #endregion + + #region ConnectionStates. + /// + /// Gets the address of a remote connection Id. + /// + /// + /// + public override string GetConnectionAddress(int connectionId) + { + return _server.GetConnectionAddress(connectionId); + } + /// + /// Called when a connection state changes for the local client. + /// + public override event Action OnClientConnectionState; + /// + /// Called when a connection state changes for the local server. + /// + public override event Action OnServerConnectionState; + /// + /// Called when a connection state changes for a remote client. + /// + public override event Action OnRemoteConnectionState; + /// + /// Gets the current local ConnectionState. + /// + /// True if getting ConnectionState for the server. + public override LocalConnectionState GetConnectionState(bool server) + { + if (server) + return _server.GetConnectionState(); + else + return _client.GetConnectionState(); + } + /// + /// Gets the current ConnectionState of a remote client on the server. + /// + /// ConnectionId to get ConnectionState for. + public override RemoteConnectionState GetConnectionState(int connectionId) + { + return _server.GetConnectionState(connectionId); + } + /// + /// Handles a ConnectionStateArgs for the local client. + /// + /// + public override void HandleClientConnectionState(ClientConnectionStateArgs connectionStateArgs) + { + OnClientConnectionState?.Invoke(connectionStateArgs); + } + /// + /// Handles a ConnectionStateArgs for the local server. + /// + /// + public override void HandleServerConnectionState(ServerConnectionStateArgs connectionStateArgs) + { + OnServerConnectionState?.Invoke(connectionStateArgs); + UpdateTimeout(); + } + /// + /// Handles a ConnectionStateArgs for a remote client. + /// + /// + public override void HandleRemoteConnectionState(RemoteConnectionStateArgs connectionStateArgs) + { + OnRemoteConnectionState?.Invoke(connectionStateArgs); + } + #endregion + + #region Iterating. + /// + /// Processes data received by the socket. + /// + /// True to process data received on the server. + public override void IterateIncoming(bool server) + { + if (server) + _server.IterateIncoming(); + else + _client.IterateIncoming(); + } + + /// + /// Processes data to be sent by the socket. + /// + /// True to process data received on the server. + public override void IterateOutgoing(bool server) + { + if (server) + _server.IterateOutgoing(); + else + _client.IterateOutgoing(); + } + #endregion + + #region ReceivedData. + /// + /// Called when client receives data. + /// + public override event Action OnClientReceivedData; + /// + /// Handles a ClientReceivedDataArgs. + /// + /// + public override void HandleClientReceivedDataArgs(ClientReceivedDataArgs receivedDataArgs) + { + OnClientReceivedData?.Invoke(receivedDataArgs); + } + /// + /// Called when server receives data. + /// + public override event Action OnServerReceivedData; + /// + /// Handles a ClientReceivedDataArgs. + /// + /// + public override void HandleServerReceivedDataArgs(ServerReceivedDataArgs receivedDataArgs) + { + OnServerReceivedData?.Invoke(receivedDataArgs); + } + #endregion + + #region Sending. + /// + /// Sends to the server or all clients. + /// + /// Channel to use. + /// Data to send. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void SendToServer(byte channelId, ArraySegment segment) + { + SanitizeChannel(ref channelId); + _client.SendToServer(channelId, segment); + } + /// + /// Sends data to a client. + /// + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void SendToClient(byte channelId, ArraySegment segment, int connectionId) + { + SanitizeChannel(ref channelId); + _server.SendToClient(channelId, segment, connectionId); + } + #endregion + + #region Configuration. + /// + /// Sets which PacketLayer to use with LiteNetLib. + /// + /// + public void SetPacketLayer(PacketLayerBase packetLayer) + { + _packetLayer = packetLayer; + if (GetConnectionState(true) != LocalConnectionState.Stopped) + base.NetworkManager.LogWarning("PacketLayer is set but will not be applied until the server stops."); + if (GetConnectionState(false) != LocalConnectionState.Stopped) + base.NetworkManager.LogWarning("PacketLayer is set but will not be applied until the client stops."); + + _server.Initialize(this, _unreliableMTU, _packetLayer); + _client.Initialize(this, _unreliableMTU, _packetLayer); + } + /// + /// How long in seconds until either the server or client socket must go without data before being timed out. + /// + /// True to get the timeout for the server socket, false for the client socket. + /// + public override float GetTimeout(bool asServer) + { + //Server and client uses the same timeout. + return (float)_timeout; + } + /// + /// Sets how long in seconds until either the server or client socket must go without data before being timed out. + /// + /// True to set the timeout for the server socket, false for the client socket. + public override void SetTimeout(float value, bool asServer) + { + _timeout = (ushort)value; + } + /// + /// Returns the maximum number of clients allowed to connect to the server. If the transport does not support this method the value -1 is returned. + /// + /// + public override int GetMaximumClients() + { + return _server.GetMaximumClients(); + } + /// + /// Sets maximum number of clients allowed to connect to the server. If applied at runtime and clients exceed this value existing clients will stay connected but new clients may not connect. + /// + /// + public override void SetMaximumClients(int value) + { + _maximumClients = value; + _server.SetMaximumClients(value); + } + /// + /// Sets which address the client will connect to. + /// + /// + public override void SetClientAddress(string address) + { + _clientAddress = address; + } + /// + /// Gets which address the client will connect to. + /// + public override string GetClientAddress() + { + return _clientAddress; + } + + /// + /// Sets which address the server will bind to. + /// + /// + public override void SetServerBindAddress(string address, IPAddressType addressType) + { + if (addressType == IPAddressType.IPv4) + _ipv4BindAddress = address; + else + _ipv6BindAddress = address; + } + /// + /// Gets which address the server will bind to. + /// + /// + public override string GetServerBindAddress(IPAddressType addressType) + { + if (addressType == IPAddressType.IPv4) + return _ipv4BindAddress; + else + return _ipv6BindAddress; + } + /// + /// Sets which port to use. + /// + /// + public override void SetPort(ushort port) + { + _port = port; + } + /// + /// Gets which port to use. + /// + /// + public override ushort GetPort() + { + return _port; + } + #endregion + + #region Start and stop. + /// + /// Starts the local server or client using configured settings. + /// + /// True to start server. + public override bool StartConnection(bool server) + { + if (server) + return StartServer(); + else + return StartClient(_clientAddress); + } + + /// + /// Stops the local server or client. + /// + /// True to stop server. + public override bool StopConnection(bool server) + { + if (server) + return StopServer(); + else + return StopClient(); + } + + /// + /// Stops a remote client from the server, disconnecting the client. + /// + /// ConnectionId of the client to disconnect. + /// True to abrutly stop the client socket. The technique used to accomplish immediate disconnects may vary depending on the transport. + /// When not using immediate disconnects it's recommended to perform disconnects using the ServerManager rather than accessing the transport directly. + /// + public override bool StopConnection(int connectionId, bool immediately) + { + return _server.StopConnection(connectionId); + } + + /// + /// Stops both client and server. + /// + public override void Shutdown() + { + //Stops client then server connections. + StopConnection(false); + StopConnection(true); + } + + #region Privates. + /// + /// Starts server. + /// + private bool StartServer() + { + _server.Initialize(this, _unreliableMTU, _packetLayer); + UpdateTimeout(); + return _server.StartConnection(_port, _maximumClients, _ipv4BindAddress, _ipv6BindAddress); + } + + /// + /// Stops server. + /// + private bool StopServer() + { + return _server.StopConnection(); + } + + /// + /// Starts the client. + /// + /// + private bool StartClient(string address) + { + _client.Initialize(this, _unreliableMTU, _packetLayer); + UpdateTimeout(); + return _client.StartConnection(address, _port); + } + + /// + /// Updates clients timeout values. + /// + private void UpdateTimeout() + { + //If server is running set timeout to max. This is for host only. + //int timeout = (GetConnectionState(true) != LocalConnectionState.Stopped) ? MAX_TIMEOUT_SECONDS : _timeout; + int timeout = (Application.isEditor) ? MAX_TIMEOUT_SECONDS : _timeout; + _client.UpdateTimeout(timeout); + _server.UpdateTimeout(timeout); + } + /// + /// Stops the client. + /// + private bool StopClient() + { + return _client.StopConnection(); + } + #endregion + #endregion + + #region Channels. + /// + /// If channelId is invalid then channelId becomes forced to reliable. + /// + /// + private void SanitizeChannel(ref byte channelId) + { + if (channelId < 0 || channelId >= TransportManager.CHANNEL_COUNT) + { + NetworkManager.LogWarning($"Channel of {channelId} is out of range of supported channels. Channel will be defaulted to reliable."); + channelId = 0; + } + } + /// + /// Gets the MTU for a channel. This should take header size into consideration. + /// For example, if MTU is 1200 and a packet header for this channel is 10 in size, this method should return 1190. + /// + /// + /// + public override int GetMTU(byte channel) + { + return _unreliableMTU; + } + #endregion + + #region Editor. +#if UNITY_EDITOR + private void OnValidate() + { + if (_unreliableMTU < 0) + _unreliableMTU = MINIMUM_UDP_MTU; + else if (_unreliableMTU > MAXIMUM_UDP_MTU) + _unreliableMTU = MAXIMUM_UDP_MTU; + } +#endif + #endregion + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Tugboat.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Tugboat.cs.meta new file mode 100644 index 0000000..cb42e47 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Tugboat.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6f48f002b825cbd45a19bd96d90f9edb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility.meta b/Assets/FishNet/Runtime/Utility.meta new file mode 100644 index 0000000..8cca6c1 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b01cb2614bcfb9249b6c78abcf482943 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/ApplicationState.cs b/Assets/FishNet/Runtime/Utility/ApplicationState.cs new file mode 100644 index 0000000..1efa14b --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/ApplicationState.cs @@ -0,0 +1,64 @@ +using FishNet.Utility.Constant; +using System.Runtime.CompilerServices; +using UnityEngine; +#if UNITY_EDITOR +using UnityEditor; +#endif + + + +namespace FishNet.Utility +{ +#if UNITY_EDITOR + [InitializeOnLoad] +#endif + public static class ApplicationState + { + +#if !UNITY_EDITOR + /// + /// True if application is quitting. + /// + private static bool _isQuitting; +#endif + static ApplicationState() + { +#if !UNITY_EDITOR + _isQuitting = false; +#endif + Application.quitting -= Application_quitting; + Application.quitting += Application_quitting; + } + + private static void Application_quitting() + { +#if !UNITY_EDITOR + _isQuitting = true; +#endif + } + + public static bool IsQuitting() + { +#if UNITY_EDITOR + if (!EditorApplication.isPlayingOrWillChangePlaymode && EditorApplication.isPlaying) + return true; + else + return false; +#else + return _isQuitting; +#endif + } + + public static bool IsPlaying() + { +#if UNITY_EDITOR + return EditorApplication.isPlaying; +#else + return Application.isPlaying; +#endif + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/ApplicationState.cs.meta b/Assets/FishNet/Runtime/Utility/ApplicationState.cs.meta new file mode 100644 index 0000000..7861977 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/ApplicationState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 54eb82a57a65e8548b57f5ca2a62bb76 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Constants.cs b/Assets/FishNet/Runtime/Utility/Constants.cs new file mode 100644 index 0000000..4da2021 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Constants.cs @@ -0,0 +1,12 @@ +namespace FishNet.Utility.Constant +{ + internal static class UtilityConstants + { + public const string CODEGEN_ASSEMBLY_NAME = "Unity.FishNet.CodeGen"; + public const string GENERATED_ASSEMBLY_NAME = "FishNet.Generated"; + public const string DEMOS_ASSEMBLY_NAME = "FishNet.Demos"; + public const string TEST_ASSEMBLY_NAME = "FishNet.Test"; + public const string RUNTIME_ASSEMBLY_NAME = "FishNet.Runtime"; + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/Constants.cs.meta b/Assets/FishNet/Runtime/Utility/Constants.cs.meta new file mode 100644 index 0000000..ef98347 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Constants.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3f2a3c23b44e4ef4e9783ef53ec0d5da +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/DDOLFinder.cs b/Assets/FishNet/Runtime/Utility/DDOLFinder.cs new file mode 100644 index 0000000..4c0118c --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/DDOLFinder.cs @@ -0,0 +1,60 @@ +using UnityEngine; + +namespace FishNet.Utility +{ + + + public class DDOLFinder : MonoBehaviour + { + #region Public. + /// + /// Singleton instance of this class. + /// + public static DDOLFinder Instance { get; private set; } + #endregion + + private void Awake() + { + FirstInitialize(); + } + + /// + /// Initializes this script for use. Should only be completed once. + /// + private void FirstInitialize() + { + if (Instance != null && Instance != this) + { + Debug.LogError("Multiple DDOL scripts found. There should be only one."); + return; + } + else + { + Instance = this; + gameObject.name = "DDOLFinder"; + DontDestroyOnLoad(gameObject); + } + } + + /// + /// Returns the current DDOL or creates one if not yet created. + /// + public static DDOLFinder GetDDOL() + { + //Not yet made. + if (Instance == null) + { + GameObject obj = new GameObject(); + DDOLFinder ddol = obj.AddComponent(); + return ddol; + } + //Already made. + else + { + return Instance; + } + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/DDOLFinder.cs.meta b/Assets/FishNet/Runtime/Utility/DDOLFinder.cs.meta new file mode 100644 index 0000000..bf2dcdb --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/DDOLFinder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 47bed61fc24f71942a7437612621bbfd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Editor.meta b/Assets/FishNet/Runtime/Utility/Editor.meta new file mode 100644 index 0000000..3d5a891 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 66122940cd5d35e49908ec08d1daf7db +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Editor/SceneDrawer.cs b/Assets/FishNet/Runtime/Utility/Editor/SceneDrawer.cs new file mode 100644 index 0000000..147a9b4 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Editor/SceneDrawer.cs @@ -0,0 +1,51 @@ +#if UNITY_EDITOR +using UnityEditor; +using UnityEngine; + +namespace FishNet.Utility.Editing +{ + /* Source https://forum.unity.com/threads/how-to-link-scenes-in-the-inspector.383140/ */ + + [CustomPropertyDrawer(typeof(SceneAttribute))] + public class SceneDrawer : PropertyDrawer + { + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + if (property.propertyType == SerializedPropertyType.String) + { + SceneAsset sceneObject = AssetDatabase.LoadAssetAtPath(property.stringValue); + + if (sceneObject == null && !string.IsNullOrEmpty(property.stringValue)) + { + // try to load it from the build settings for legacy compatibility + sceneObject = GetBuildSettingsSceneObject(property.stringValue); + } + if (sceneObject == null && !string.IsNullOrEmpty(property.stringValue)) + { + Debug.Log($"Could not find scene {property.stringValue} in {property.propertyPath}, assign the proper scenes in your NetworkManager"); + } + SceneAsset scene = (SceneAsset)EditorGUI.ObjectField(position, label, sceneObject, typeof(SceneAsset), true); + + property.stringValue = AssetDatabase.GetAssetPath(scene); + } + else + { + EditorGUI.LabelField(position, label.text, "Use [Scene] with strings."); + } + } + + protected SceneAsset GetBuildSettingsSceneObject(string sceneName) + { + foreach (EditorBuildSettingsScene buildScene in EditorBuildSettings.scenes) + { + SceneAsset sceneAsset = AssetDatabase.LoadAssetAtPath(buildScene.path); + if (sceneAsset != null && sceneAsset.name == sceneName) + { + return sceneAsset; + } + } + return null; + } + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/Editor/SceneDrawer.cs.meta b/Assets/FishNet/Runtime/Utility/Editor/SceneDrawer.cs.meta new file mode 100644 index 0000000..8f3c47d --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Editor/SceneDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2b2c813205b39ed46953611f7a5659fd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Extension.meta b/Assets/FishNet/Runtime/Utility/Extension.meta new file mode 100644 index 0000000..bb462bb --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8f30057a48bb0104d8a7813443607804 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Extension/Bool.cs b/Assets/FishNet/Runtime/Utility/Extension/Bool.cs new file mode 100644 index 0000000..1907675 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Bool.cs @@ -0,0 +1,18 @@ + +namespace FishNet.Utility.Extension +{ + public static class BooleanExtensions + { + /// + /// Converts a boolean to an integer. + /// + /// + /// + public static int ToInt(this bool b) + { + return (b) ? 1 : 0; + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/Extension/Bool.cs.meta b/Assets/FishNet/Runtime/Utility/Extension/Bool.cs.meta new file mode 100644 index 0000000..807bccb --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Bool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c688c9bd497f4a749b692b9b1d628c51 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Extension/Collection.cs b/Assets/FishNet/Runtime/Utility/Extension/Collection.cs new file mode 100644 index 0000000..f1c2ae2 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Collection.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; + +namespace FishNet.Utility.Extension +{ + public static class CollectionFN + { + /// + /// Random for shuffling. + /// + private static Random _random = new Random(); + + /// + /// Shuffle based on Fisher-Yates shuffle. + /// https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle + /// https://stackoverflow.com/questions/273313/randomize-a-listt + /// + public static void Shuffle(this IList lst) + { + int n = lst.Count; + while (n > 1) + { + n--; + int k = _random.Next(n + 1); + T value = lst[k]; + lst[k] = lst[n]; + lst[n] = value; + } + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/Extension/Collection.cs.meta b/Assets/FishNet/Runtime/Utility/Extension/Collection.cs.meta new file mode 100644 index 0000000..0fa0ef3 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Collection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9a6539089deb687469d1abdc1b8964a1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Extension/Dictionary.cs b/Assets/FishNet/Runtime/Utility/Extension/Dictionary.cs new file mode 100644 index 0000000..4f9a5f4 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Dictionary.cs @@ -0,0 +1,35 @@ +using FishNet.Documenting; +using System.Collections.Generic; + +namespace FishNet.Utility.Extension +{ + [APIExclude] + public static class DictionaryFN + { + + /// + /// Uses a hacky way to TryGetValue on a dictionary when using IL2CPP and on mobile. + /// This is to support older devices that don't properly handle IL2CPP builds. + /// + public static bool TryGetValueIL2CPP(this IDictionary dict, TKey key, out TValue value) + { +#if ENABLE_IL2CPP && UNITY_IOS || UNITY_ANDROID + if (dict.ContainsKey(key)) + { + value = dict[key]; + return true; + } + else + { + value = default; + return false; + } +#else + return dict.TryGetValue(key, out value); +#endif + } + + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/Extension/Dictionary.cs.meta b/Assets/FishNet/Runtime/Utility/Extension/Dictionary.cs.meta new file mode 100644 index 0000000..67297e5 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Dictionary.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d286695e7464ce943bc321215aaa2ee1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Extension/Enum.cs b/Assets/FishNet/Runtime/Utility/Extension/Enum.cs new file mode 100644 index 0000000..246d34b --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Enum.cs @@ -0,0 +1,31 @@ +using System; + +namespace FishNet.Utility.Extension +{ + public static class EnumFN + { + + /// + /// Returns the highest numeric value for T. + /// + public static int GetHighestValue() + { + Type enumType = typeof(T); + /* Brute force enum values. + * Linq Last/Max lookup throws for IL2CPP. */ + int highestValue = 0; + Array pidValues = Enum.GetValues(enumType); + foreach (T pid in pidValues) + { + object obj = Enum.Parse(enumType, pid.ToString()); + int value = Convert.ToInt32(obj); + highestValue = Math.Max(highestValue, value); + } + + return highestValue; + } + + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/Extension/Enum.cs.meta b/Assets/FishNet/Runtime/Utility/Extension/Enum.cs.meta new file mode 100644 index 0000000..d4b39d4 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Enum.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f58b410f20b8e694aa852d2ea5240626 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Extension/List.cs b/Assets/FishNet/Runtime/Utility/Extension/List.cs new file mode 100644 index 0000000..ede1347 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/List.cs @@ -0,0 +1,24 @@ +using FishNet.Documenting; +using System.Collections.Generic; + +namespace FishNet.Utility.Extension +{ + [APIExclude] + public static class ListFN + { + + /// + /// Adds a value to the list only if the value does not already exist. + /// + /// Collection being added to. + /// Value to add. + public static void AddUnique(this List lst, T value) + { + if (!lst.Contains(value)) + lst.Add(value); + } + + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/Extension/List.cs.meta b/Assets/FishNet/Runtime/Utility/Extension/List.cs.meta new file mode 100644 index 0000000..82b83a8 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/List.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 953eb4b102d504544b49087104e6b747 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Extension/Math.cs b/Assets/FishNet/Runtime/Utility/Extension/Math.cs new file mode 100644 index 0000000..84d97e6 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Math.cs @@ -0,0 +1,34 @@ +namespace FishNet.Utility.Extension +{ + + public static class MathFN + { + + /// + /// Returns a clamped SBytte. + /// + public static sbyte ClampSByte(long value, sbyte min, sbyte max) + { + if (value < min) + return min; + else if (value > max) + return max; + else + return (sbyte)value; + } + + /// + /// Returns a clamped double. + /// + public static double ClampDouble(double value, double min, double max) + { + if (value < min) + return min; + else if (value > max) + return max; + else + return value; + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/Extension/Math.cs.meta b/Assets/FishNet/Runtime/Utility/Extension/Math.cs.meta new file mode 100644 index 0000000..2df9023 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Math.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: feb978e97a6aa6b4cbb99481d925c00c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Extension/Object.cs b/Assets/FishNet/Runtime/Utility/Extension/Object.cs new file mode 100644 index 0000000..6ec0ff5 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Object.cs @@ -0,0 +1,27 @@ +using FishNet.Connection; +using FishNet.Object; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Utility.Extension +{ + + public static class ObjectFN + { + /// + /// Spawns an object over the network using InstanceFinder. Only call from the server. + /// + public static void Spawn(this NetworkObject nob, NetworkConnection owner = null) + { + InstanceFinder.ServerManager.Spawn(nob, owner); + } + /// + /// Spawns an object over the network using InstanceFinder. Only call from the server. + /// + public static void Spawn(this GameObject go, NetworkConnection owner = null) + { + InstanceFinder.ServerManager.Spawn(go, owner); + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/Extension/Object.cs.meta b/Assets/FishNet/Runtime/Utility/Extension/Object.cs.meta new file mode 100644 index 0000000..6d573f8 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Object.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3d1cdee4c45e73a4fa9adba1177483ca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Extension/Quaternion.cs b/Assets/FishNet/Runtime/Utility/Extension/Quaternion.cs new file mode 100644 index 0000000..5f5e69d --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Quaternion.cs @@ -0,0 +1,43 @@ +using FishNet.Documenting; +using UnityEngine; + +namespace FishNet.Utility.Extension +{ + [APIExclude] + public static class QuaternionFN + { + + /// + /// Returns if two quaternions match. + /// + /// True to use a custom implementation with no error tolerance. False to use Unity's implementation which may return a match even when not true due to error tolerance. + /// + public static bool Matches(this Quaternion a, Quaternion b, bool precise = false) + { + if (precise) + return (a.w == b.w && a.x == b.x && a.y == b.y && a.z == b.z); + else + return (a == b); + } + + /// + /// Returns the angle between two quaterions. + /// + /// True to use a custom implementation with no error tolerance. False to use Unity's implementation which may return 0f due to error tolerance, even while there is a difference. + /// + public static float Angle(this Quaternion a, Quaternion b, bool precise = false) + { + if (precise) + { + //This is run Unitys implementation without the error tolerance. + float dot = (a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w); + return (Mathf.Acos(Mathf.Min(Mathf.Abs(dot), 1f)) * 2f * 57.29578f); + } + else + { + return Quaternion.Angle(a, b); + } + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/Extension/Quaternion.cs.meta b/Assets/FishNet/Runtime/Utility/Extension/Quaternion.cs.meta new file mode 100644 index 0000000..4bdf137 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Quaternion.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4b8122e8a7592784896b4173707188ce +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Extension/Scene.cs b/Assets/FishNet/Runtime/Utility/Extension/Scene.cs new file mode 100644 index 0000000..1ea499b --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Scene.cs @@ -0,0 +1,76 @@ +using FishNet.Object; +using FishNet.Utility.Performance; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace FishNet.Utility.Extension +{ + + public static class SceneFN + { + #region Private. + + /// + /// Used for performance gains when getting objects. + /// + private static List _gameObjectList = new List(); + /// + /// List for NetworkObjects. + /// + private static List _networkObjectListA = new List(); + /// + /// List for NetworkObjects. + /// + private static List _networkObjectListB = new List(); + #endregion + + /// + /// Gets all NetworkObjects in a scene. + /// + /// Scene to get objects in. + /// True to only return the first NetworkObject within an object chain. False will return nested NetworkObjects. + /// ListCache of found NetworkObjects. + /// + public static void GetSceneNetworkObjects(Scene s, bool firstOnly, out ListCache nobCache) + { + nobCache = ListCaches.GetNetworkObjectCache(); + //Iterate all root objects for the scene. + s.GetRootGameObjects(_gameObjectList); + foreach (GameObject go in _gameObjectList) + { + + //Get NetworkObjects within children of each root. + go.GetComponentsInChildren(true, _networkObjectListA); + //If network objects are found. + if (_networkObjectListA.Count > 0) + { + //Add only the first networkobject + if (firstOnly) + { + /* The easiest way to see if a nob is nested is to + * get nobs in parent and if the count is greater than 1, then + * it is nested. The technique used here isn't exactly fast but + * it will only occur during scene loads, so I'm trading off speed + * for effort and readability. */ + foreach (NetworkObject nob in _networkObjectListA) + { + nob.GetComponentsInParent(true, _networkObjectListB); + //No extra nobs, only this one. + if (_networkObjectListB.Count == 1) + nobCache.AddValue(nob); + } + } + //Not first only, add them all. + else + { + nobCache.AddValues(_networkObjectListA); + } + + } + } + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/Extension/Scene.cs.meta b/Assets/FishNet/Runtime/Utility/Extension/Scene.cs.meta new file mode 100644 index 0000000..e079485 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Scene.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a02f3d03f737e304e9854278f4e9211d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Extension/Transforms.cs b/Assets/FishNet/Runtime/Utility/Extension/Transforms.cs new file mode 100644 index 0000000..d264059 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Transforms.cs @@ -0,0 +1,42 @@ +using FishNet.Documenting; +using UnityEngine; + +namespace FishNet.Utility.Extension +{ + [APIExclude] + public static class TransformFN + { + /// + /// Sets the offset values of target from a transform. + /// + /// Position offset result. + /// Rotation offset result. + public static void SetTransformOffsets(this Transform t, Transform target, ref Vector3 pos, ref Quaternion rot) + { + if (target == null) + return; + pos = (t.position - target.position); + rot = (t.rotation * Quaternion.Inverse(target.rotation)); + } + + /// + /// Sets local position and rotation for a transform. + /// + public static void SetLocalPositionAndRotation(this Transform t, Vector3 pos, Quaternion rot) + { + t.localPosition = pos; + t.localRotation = rot; + } + /// + /// Sets local position, rotation, and scale for a transform. + /// + public static void SetLocalPositionRotationAndScale(this Transform t, Vector3 pos, Quaternion rot, Vector3 scale) + { + t.localPosition = pos; + t.localRotation = rot; + t.localScale = scale; + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/Extension/Transforms.cs.meta b/Assets/FishNet/Runtime/Utility/Extension/Transforms.cs.meta new file mode 100644 index 0000000..bdee36b --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Transforms.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3d311fc1bf09b9e4fbc5a17a9c50ab0d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Performance.meta b/Assets/FishNet/Runtime/Utility/Performance.meta new file mode 100644 index 0000000..d114a4d --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c297487b42ef1b640a26b7c41fef6e27 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Performance/ByteArrayPool.cs b/Assets/FishNet/Runtime/Utility/Performance/ByteArrayPool.cs new file mode 100644 index 0000000..9adcc58 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/ByteArrayPool.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; + +namespace FishNet.Utility.Performance +{ + + /// + /// Retrieves and stores byte arrays using a pooling system. + /// + public static class ByteArrayPool + { + /// + /// Stored byte arrays. + /// + private static Queue _byteArrays = new Queue(); + + /// + /// Returns a byte array which will be of at lesat minimum length. The returned array must manually be stored. + /// + public static byte[] Retrieve(int minimumLength) + { + byte[] result = null; + + if (_byteArrays.Count > 0) + result = _byteArrays.Dequeue(); + + int doubleMinimumLength = (minimumLength * 2); + if (result == null) + result = new byte[doubleMinimumLength]; + else if (result.Length < minimumLength) + Array.Resize(ref result, doubleMinimumLength); + + return result; + } + + /// + /// Stores a byte array for re-use. + /// + public static void Store(byte[] buffer) + { + /* Holy cow that's a lot of buffered + * buffers. This wouldn't happen under normal + * circumstances but if the user is stress + * testing connections in one executable perhaps. */ + if (_byteArrays.Count > 300) + return; + _byteArrays.Enqueue(buffer); + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/Performance/ByteArrayPool.cs.meta b/Assets/FishNet/Runtime/Utility/Performance/ByteArrayPool.cs.meta new file mode 100644 index 0000000..4c5401f --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/ByteArrayPool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c7620a5e6fedc18408f8f04821b35bbd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Performance/DefaultObjectPool.cs b/Assets/FishNet/Runtime/Utility/Performance/DefaultObjectPool.cs new file mode 100644 index 0000000..ac01d71 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/DefaultObjectPool.cs @@ -0,0 +1,203 @@ +using FishNet.Managing.Object; +using FishNet.Object; +using FishNet.Utility.Extension; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Utility.Performance +{ + + + public class DefaultObjectPool : ObjectPool + { + #region Public. + /// + /// Cache for pooled NetworkObjects. + /// + public IReadOnlyCollection>> Cache => _cache; + private List>> _cache = new List>>(); + #endregion + + #region Serialized. + /// + /// True if to use object pooling. + /// + [Tooltip("True if to use object pooling.")] + [SerializeField] + private bool _enabled = true; + #endregion + + #region Private. + /// + /// Current count of the cache collection. + /// + private int _cacheCount = 0; + #endregion + + /// + /// Returns an object that has been stored with a collectionId of 0. A new object will be created if no stored objects are available. + /// + /// PrefabId of the object to return. + /// True if being called on the server side. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override NetworkObject RetrieveObject(int prefabId, bool asServer) + { + return RetrieveObject(prefabId, 0, asServer); + } + + /// + /// Returns an object that has been stored. A new object will be created if no stored objects are available. + /// + /// PrefabId of the object to return. + /// CollectionId of the prefab. + /// True if being called on the server side. + /// + public override NetworkObject RetrieveObject(int prefabId, ushort collectionId, bool asServer) + { + PrefabObjects po = base.NetworkManager.GetPrefabObjects(collectionId, false); + //Quick exit/normal retrieval when not using pooling. + if (!_enabled) + { + NetworkObject prefab = po.GetObject(asServer, prefabId); + return Instantiate(prefab); + } + + Stack cache = GetOrCreateCache(collectionId, prefabId); + NetworkObject nob; + //Iterate until nob is populated just in case cache entries have been destroyed. + do + { + if (cache.Count == 0) + { + NetworkObject prefab = po.GetObject(asServer, prefabId); + /* A null nob should never be returned from spawnables. This means something + * else broke, likely unrelated to the object pool. */ + nob = Instantiate(prefab); + //Can break instantly since we know nob is not null. + break; + } + else + { + nob = cache.Pop(); + } + + } while (nob == null); + + nob.gameObject.SetActive(true); + return nob; + } + /// + /// Stores an object into the pool. + /// + /// Object to store. + /// True if being called on the server side. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void StoreObject(NetworkObject instantiated, bool asServer) + { + //Pooling is not enabled. + if (!_enabled) + { + Destroy(instantiated.gameObject); + return; + } + + instantiated.gameObject.SetActive(false); + instantiated.ResetForObjectPool(); + Stack cache = GetOrCreateCache(instantiated.SpawnableCollectionId, instantiated.PrefabId); + cache.Push(instantiated); + } + + /// + /// Instantiates a number of objects and adds them to the pool. + /// + /// Prefab to cache. + /// Quantity to spawn. + /// True if storing prefabs for the server collection. This is only applicable when using DualPrefabObjects. + public void CacheObjects(NetworkObject prefab, int count, bool asServer) + { + if (!_enabled) + return; + if (count <= 0) + return; + if (prefab == null) + return; + if (prefab.PrefabId == NetworkObject.UNSET_PREFABID_VALUE) + { + InstanceFinder.NetworkManager.LogError($"Pefab {prefab.name} has an invalid prefabId and cannot be cached."); + return; + } + + Stack cache = GetOrCreateCache(prefab.SpawnableCollectionId, prefab.PrefabId); + for (int i = 0; i < count; i++) + { + NetworkObject nob = Instantiate(prefab); + nob.gameObject.SetActive(false); + cache.Push(nob); + } + } + + /// + /// Clears pools for all collectionIds + /// + public void ClearPool() + { + int count = _cache.Count; + for (int i = 0; i < count; i++) + ClearPool(i); + } + + /// + /// Clears a pool for collectionId. + /// + /// CollectionId to clear for. + public void ClearPool(int collectionId) + { + if (collectionId >= _cacheCount) + return; + + Dictionary> dict = _cache[collectionId]; + //Convert to a list from the stack so we do not modify the stack directly. + ListCache nobCache = ListCaches.GetNetworkObjectCache(); + foreach (Stack item in dict.Values) + { + while (item.Count > 0) + nobCache.AddValue(item.Pop()); + } + } + + + /// + /// Gets a cache for an id or creates one if does not exist. + /// + /// + /// + private Stack GetOrCreateCache(int collectionId, int prefabId) + { + if (collectionId >= _cacheCount) + { + //Add more to the cache. + while (_cache.Count <= collectionId) + { + Dictionary> dict = new Dictionary>(); + _cache.Add(dict); + } + _cacheCount = collectionId; + } + + Dictionary> dictionary = _cache[collectionId]; + Stack cache; + //No cache for prefabId yet, make one. + if (!dictionary.TryGetValueIL2CPP(prefabId, out cache)) + { + cache = new Stack(); + dictionary[prefabId] = cache; + } + return cache; + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/Performance/DefaultObjectPool.cs.meta b/Assets/FishNet/Runtime/Utility/Performance/DefaultObjectPool.cs.meta new file mode 100644 index 0000000..c88391a --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/DefaultObjectPool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cb13d174096685549b1d6a94d726ff7d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Performance/ListCache.cs b/Assets/FishNet/Runtime/Utility/Performance/ListCache.cs new file mode 100644 index 0000000..d252efa --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/ListCache.cs @@ -0,0 +1,341 @@ +using FishNet.Connection; +using FishNet.Managing; +using FishNet.Object; +using FishNet.Serializing.Helping; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Utility.Performance +{ + /// + /// Various ListCache instances that may be used on the Unity thread. + /// + public static class ListCaches + { + + /// + /// Cache collection for NetworkObjects. + /// + private static Stack> _networkObjectCaches = new Stack>(); + /// + /// Cache collection for NetworkObjects. + /// + private static Stack> _networkBehaviourCaches = new Stack>(); + /// + /// Cache collection for NetworkObjects. + /// + private static Stack> _transformCaches = new Stack>(); + /// + /// Cache collection for NetworkConnections. + /// + private static Stack> _networkConnectionCaches = new Stack>(); + /// + /// Cache collection for ints. + /// + private static Stack> _intCaches = new Stack>(); + + + #region GetCache. + /// + /// Returns a NetworkObject cache. Use StoreCache to return the cache. + /// + /// + public static ListCache GetNetworkObjectCache() + { + ListCache result; + if (_networkObjectCaches.Count == 0) + result = new ListCache(); + else + result = _networkObjectCaches.Pop(); + + return result; + } + /// + /// Returns a NetworkConnection cache. Use StoreCache to return the cache. + /// + /// + public static ListCache GetNetworkConnectionCache() + { + ListCache result; + if (_networkConnectionCaches.Count == 0) + result = new ListCache(); + else + result = _networkConnectionCaches.Pop(); + + return result; + } + /// + /// Returns a Transform cache. Use StoreCache to return the cache. + /// + /// + public static ListCache GetTransformCache() + { + ListCache result; + if (_transformCaches.Count == 0) + result = new ListCache(); + else + result = _transformCaches.Pop(); + + return result; + } + /// + /// Returns a NetworkBehaviour cache. Use StoreCache to return the cache. + /// + /// + public static ListCache GetNetworkBehaviourCache() + { + ListCache result; + if (_networkBehaviourCaches.Count == 0) + result = new ListCache(); + else + result = _networkBehaviourCaches.Pop(); + + return result; + } + /// + /// Returns an int cache. Use StoreCache to return the cache. + /// + /// + public static ListCache GetIntCache() + { + ListCache result; + if (_intCaches.Count == 0) + result = new ListCache(); + else + result = _intCaches.Pop(); + + return result; + } + #endregion + + #region StoreCache. + /// + /// Stores a NetworkObject cache. + /// + /// + public static void StoreCache(ListCache cache) + { + cache.Reset(); + _networkObjectCaches.Push(cache); + } + /// + /// Stores a NetworkConnection cache. + /// + /// + public static void StoreCache(ListCache cache) + { + cache.Reset(); + _networkConnectionCaches.Push(cache); + } + /// + /// Stores a Transform cache. + /// + /// + public static void StoreCache(ListCache cache) + { + cache.Reset(); + _transformCaches.Push(cache); + } + /// + /// Stores a NetworkBehaviour cache. + /// + /// + public static void StoreCache(ListCache cache) + { + cache.Reset(); + _networkBehaviourCaches.Push(cache); + } + /// + /// Stores an int cache. + /// + /// + public static void StoreCache(ListCache cache) + { + cache.Reset(); + _intCaches.Push(cache); + } + #endregion + + } + + /// + /// Creates a reusable cache of T which auto expands. + /// + public class ListCache + { + #region Public. + /// + /// Collection cache is for. + /// + public List Collection = new List(); + /// + /// Entries currently written. + /// + public int Written => Collection.Count; + #endregion + + #region Private. + /// + /// Cache for type. + /// + private Stack _cache = new Stack(); + #endregion + + public ListCache() + { + Collection = new List(); + } + public ListCache(int capacity) + { + Collection = new List(capacity); + } + + /// + /// Returns T from cache when possible, or creates a new object when not. + /// + /// + private T Retrieve() + { + if (_cache.Count > 0) + return _cache.Pop(); + else + return Activator.CreateInstance(); + } + /// + /// Stores value into the cache. + /// + /// + private void Store(T value) + { + _cache.Push(value); + } + + /// + /// Adds a new value to Collection and returns it. + /// + /// + public T AddReference() + { + T next = Retrieve(); + Collection.Add(next); + return next; + } + + /// + /// Inserts an bject into Collection and returns it. + /// + /// + public T InsertReference(int index) + { + //Would just be at the end anyway. + if (index >= Collection.Count) + return AddReference(); + + T next = Retrieve(); + Collection.Insert(index, next); + return next; + } + + /// + /// Adds value to Collection. + /// + /// + public void AddValue(T value) + { + Collection.Add(value); + } + + /// + /// Inserts value into Collection. + /// + /// + + public void InsertValue(int index, T value) + { + //Would just be at the end anyway. + if (index >= Collection.Count) + AddValue(value); + else + Collection.Insert(index, value); + } + + /// + /// Adds values to Collection. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddValues(ListCache values) + { + int w = values.Written; + List c = values.Collection; + for (int i = 0; i < w; i++) + AddValue(c[i]); + } + /// + /// Adds values to Collection. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddValues(T[] values) + { + for (int i = 0; i < values.Length; i++) + AddValue(values[i]); + } + /// + /// Adds values to Collection. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddValues(List values) + { + for (int i = 0; i < values.Count; i++) + AddValue(values[i]); + } + /// + /// Adds values to Collection. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddValues(HashSet values) + { + foreach (T item in values) + AddValue(item); + } + /// + /// Adds values to Collection. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddValues(ISet values) + { + foreach (T item in values) + AddValue(item); + } + + /// + /// Adds values to Collection. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddValues(IReadOnlyCollection values) + { + foreach (T item in values) + AddValue(item); + } + + + /// + /// Resets cache. + /// + public void Reset() + { + foreach (T item in Collection) + Store(item); + Collection.Clear(); + } + } + + +} diff --git a/Assets/FishNet/Runtime/Utility/Performance/ListCache.cs.meta b/Assets/FishNet/Runtime/Utility/Performance/ListCache.cs.meta new file mode 100644 index 0000000..4b13d4c --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/ListCache.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 488b0788adfd9ee43977abd5d0280124 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Performance/ObjectPool.cs b/Assets/FishNet/Runtime/Utility/Performance/ObjectPool.cs new file mode 100644 index 0000000..f35d6b4 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/ObjectPool.cs @@ -0,0 +1,47 @@ +using FishNet.Managing; +using FishNet.Object; +using System; +using UnityEngine; + +namespace FishNet.Utility.Performance +{ + + public abstract class ObjectPool : MonoBehaviour + { + /// + /// NetworkManager this ObjectPool belongs to. + /// + protected NetworkManager NetworkManager {get; private set;} + + /// + /// Initializes this script for use. + /// + public virtual void InitializeOnce(NetworkManager nm) + { + NetworkManager = nm; + } + + /// + /// Returns an object that has been stored using collectioNid of 0. A new object will be created if no stored objects are available. + /// + /// PrefabId of the object to return. + /// True if being called on the server side. + /// + public abstract NetworkObject RetrieveObject(int prefabId, bool asServer); + /// + /// Returns an object that has been stored. A new object will be created if no stored objects are available. + /// + /// PrefabId of the object to return. + /// True if being called on the server side. + /// + public virtual NetworkObject RetrieveObject(int prefabId, ushort collectionId, bool asServer) => null; + /// + /// Stores an object into the pool. + /// + /// Object to store. + /// True if being called on the server side. + /// + public abstract void StoreObject(NetworkObject instantiated, bool asServer); + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/Performance/ObjectPool.cs.meta b/Assets/FishNet/Runtime/Utility/Performance/ObjectPool.cs.meta new file mode 100644 index 0000000..e5ee86a --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/ObjectPool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6ec7d855ffa7afc45b619b84ddbda27c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Performance/RingBuffer.cs b/Assets/FishNet/Runtime/Utility/Performance/RingBuffer.cs new file mode 100644 index 0000000..0486c16 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/RingBuffer.cs @@ -0,0 +1,339 @@ +using FishNet.Documenting; +using FishNet.Managing; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Utility +{ + + /// + /// Writes values to a collection of a set size, overwriting old values as needed. + /// + public class RingBuffer + { + #region Types. + /// + /// Custom enumerator to prevent garbage collection. + /// + [APIExclude] + public struct Enumerator : IEnumerator + { + #region Public. + /// + /// Current entry in the enumerator. + /// + public T Current { get; private set; } + /// + /// Actual index of the last enumerated value. + /// + public int ActualIndex + { + get + { + int total = (_startIndex + (_read - 1)); + int capacity = _rollingCollection.Capacity; + if (total >= capacity) + total -= capacity; + + return total; + } + } + /// + /// Simulated index of the last enumerated value. + /// + public int SimulatedIndex => (_read - 1); + #endregion + + #region Private. + /// + /// RollingCollection to use. + /// + private RingBuffer _rollingCollection; + /// + /// Collection to iterate. + /// + private readonly T[] _collection; + /// + /// Number of entries read during the enumeration. + /// + private int _read; + /// + /// Start index of enumerations. + /// + private int _startIndex; + #endregion + + public Enumerator(RingBuffer c) + { + _read = 0; + _startIndex = 0; + _rollingCollection = c; + _collection = c.Collection; + Current = default; + } + + public bool MoveNext() + { + int written = _rollingCollection.Count; + if (_read >= written) + { + ResetRead(); + return false; + } + + int index = (_startIndex + _read); + int capacity = _rollingCollection.Capacity; + if (index >= capacity) + index -= capacity; + Current = _collection[index]; + + _read++; + + return true; + } + + /// + /// Sets a new start index to begin reading at. + /// + public void SetStartIndex(int index) + { + _startIndex = index; + ResetRead(); + } + + + /// + /// Sets a new start index to begin reading at. + /// + public void AddStartIndex(int value) + { + _startIndex += value; + + int cap = _rollingCollection.Capacity; + if (_startIndex > cap) + _startIndex -= cap; + else if (_startIndex < 0) + _startIndex += cap; + + ResetRead(); + } + + /// + /// Resets number of entries read during the enumeration. + /// + public void ResetRead() + { + _read = 0; + } + + /// + /// Resets read count. + /// + public void Reset() + { + _startIndex = 0; + ResetRead(); + } + + object IEnumerator.Current => Current; + public void Dispose() { } + } + + #endregion + + #region Public. + /// + /// Current write index of the collection. + /// + public int WriteIndex { get; private set; } + /// + /// Number of entries currently written. + /// + public int Count => _written; + /// + /// Maximum size of the collection. + /// + public int Capacity => Collection.Length; + /// + /// Collection being used. + /// + public T[] Collection = new T[0]; + /// + /// True if initialized. + /// + public bool Initialized { get; private set; } + #endregion + + #region Private. + /// + /// Number of entries written. This will never go beyond the capacity but will be less until capacity is filled. + /// + private int _written; + /// + /// Enumerator for the collection. + /// + private Enumerator _enumerator; + #endregion + + /// + /// Initializes the collection at length. + /// + /// Size to initialize the collection as. This cannot be changed after initialized. + public void Initialize(int capacity) + { + if (capacity <= 0) + { + NetworkManager.StaticLogError($"Collection length must be larger than 0."); + return; + } + + Collection = new T[capacity]; + _enumerator = new Enumerator(this); + Initialized = true; + } + + /// + /// Clears the collection to default values and resets indexing. + /// + public void Clear() + { + for (int i = 0; i < Collection.Length; i++) + Collection[i] = default; + + Reset(); + } + /// + /// Resets the collection without clearing. + /// + public void Reset() + { + _written = 0; + WriteIndex = 0; + _enumerator.Reset(); + } + + /// + /// Adds an entry to the collection, returning a replaced entry. + /// + /// Data to add. + /// Replaced entry. Value will be default if no entry was replaced. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Add(T data) + { + if (!IsInitializedWithError()) + return default; + + int capacity = Capacity; + T current = Collection[WriteIndex]; + Collection[WriteIndex] = data; + + WriteIndex++; + _written++; + //If index would exceed next iteration reset it. + if (WriteIndex >= capacity) + WriteIndex = 0; + + /* If written has exceeded capacity + * then the start index needs to be moved + * to adjust for overwritten values. */ + if (_written > capacity) + { + _written = capacity; + _enumerator.SetStartIndex(WriteIndex); + } + + return current; + } + + /// + /// Returns value in actual index as it relates to simulated index. + /// + /// Simulated index to return. A value of 0 would return the first simulated index in the collection. + /// + public T this[int simulatedIndex] + { + get + { + int offset = (Capacity - _written) + simulatedIndex + WriteIndex; + if (offset >= Capacity) + offset -= Capacity; + return Collection[offset]; + } + set + { + int offset = (Capacity - _written) + simulatedIndex + WriteIndex; + if (offset >= Capacity) + offset -= Capacity; + Collection[offset] = value; + } + } + + /// + /// Returns Enumerator for the collection. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() + { + if (!IsInitializedWithError()) + return default; + + _enumerator.ResetRead(); + return _enumerator; + } + + /// + /// Removes values from the simulated start of the collection. + /// + /// True to remove from the start, false to remove from the end. + /// Number of entries to remove. + public void RemoveRange(bool fromStart, int length) + { + if (length == 0) + return; + if (length < 0) + { + NetworkManager.StaticLogError($"Negative values cannot be removed."); + return; + } + //Full reset if value is at or more than written. + if (length >= _written) + { + Reset(); + return; + } + + _written -= length; + if (fromStart) + { + _enumerator.AddStartIndex(length); + } + else + { + + WriteIndex -= length; + if (WriteIndex < 0) + WriteIndex += Capacity; + } + } + + /// + /// Returns if initialized and errors if not. + /// + /// + private bool IsInitializedWithError() + { + if (!Initialized) + { + NetworkManager.StaticLogError($"RingBuffer has not yet been initialized."); + return false; + } + + return true; + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/Performance/RingBuffer.cs.meta b/Assets/FishNet/Runtime/Utility/Performance/RingBuffer.cs.meta new file mode 100644 index 0000000..05ff562 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/RingBuffer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a066c51d748a04546875bd7d43118837 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Performance/Transforms.cs b/Assets/FishNet/Runtime/Utility/Performance/Transforms.cs new file mode 100644 index 0000000..59eb1a5 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/Transforms.cs @@ -0,0 +1,39 @@ +using FishNet.Object; +using System.Collections.Generic; +using UnityEngine; + +namespace FishNet.Utility.Performance +{ + + public static class GetNonAlloc + { + /// + /// + /// + private static List _transformList = new List(); + /// + /// + /// + private static List _networkBehavioursList = new List(); + + /// + /// Gets all NetworkBehaviours on a transform. + /// + public static List GetNetworkBehaviours(this Transform t) + { + t.GetComponents(_networkBehavioursList); + return _networkBehavioursList; + } + + /// + /// Gets all transforms on transform and it's children. + /// + public static List GetTransformsInChildrenNonAlloc(this Transform t, bool includeInactive = false) + { + t.GetComponentsInChildren(includeInactive, _transformList); + return _transformList; + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/Performance/Transforms.cs.meta b/Assets/FishNet/Runtime/Utility/Performance/Transforms.cs.meta new file mode 100644 index 0000000..b2bc83c --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/Transforms.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7d0740f919077254c8ffb131b9587407 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/SceneAttribute.cs b/Assets/FishNet/Runtime/Utility/SceneAttribute.cs new file mode 100644 index 0000000..dd343a7 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/SceneAttribute.cs @@ -0,0 +1,12 @@ +using UnityEngine; + +namespace FishNet.Utility +{ + /* Source https://forum.unity.com/threads/how-to-link-scenes-in-the-inspector.383140/ */ + + /// + /// Converts a string property into a Scene property in the inspector + /// + public class SceneAttribute : PropertyAttribute { } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/SceneAttribute.cs.meta b/Assets/FishNet/Runtime/Utility/SceneAttribute.cs.meta new file mode 100644 index 0000000..5d3afce --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/SceneAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3684fb9a5dec7454b8ad791f5ef19164 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/THIRD PARTY NOTICE.md b/Assets/FishNet/THIRD PARTY NOTICE.md new file mode 100644 index 0000000..343ab60 --- /dev/null +++ b/Assets/FishNet/THIRD PARTY NOTICE.md @@ -0,0 +1,82 @@ +This package contains third-party software components governed by the license(s) indicated below: + +Component Name: LiteNetLib +License Type: MIT +Copyright (c) 2020 Ruslan Pyrch +Copyright (c) 2021, Benjamin Berwick of FirstGearGames LLC, registered 2018, North Carolina. + +Paths: FishNet\Runtime\Transporting\Transports\Tugboat\LiteNetLib + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + +Component Name: CodeGen Helpers(extensions). +License Type: MIT +Copyright (c) 2015, Unity Technologies +Copyright (c) 2019, vis2k, Paul and Contributors +Copyright (c) 2021, Benjamin Berwick of FirstGearGames LLC, registered 2018, North Carolina. + +Paths: FishNet/CodeGenerating/Helpers/ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + +Component Name: MonoCecil +License Type: MIT +Copyright (c) 2008 - 2015 Jb Evain +Copyright (c) 2008 - 2011 Novell, Inc. + +Paths: FishNet/CodeGenerating/cecil-xxxxx + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Assets/FishNet/THIRD PARTY NOTICE.md.meta b/Assets/FishNet/THIRD PARTY NOTICE.md.meta new file mode 100644 index 0000000..f9ad4bb --- /dev/null +++ b/Assets/FishNet/THIRD PARTY NOTICE.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 6ee8e3f1530d3594488bfe438dced5ea +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Upgrading.meta b/Assets/FishNet/Upgrading.meta new file mode 100644 index 0000000..96ab21b --- /dev/null +++ b/Assets/FishNet/Upgrading.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 45e9a7bff88078f49ae15609740702eb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Upgrading/MirrorUpgrade.cs b/Assets/FishNet/Upgrading/MirrorUpgrade.cs new file mode 100644 index 0000000..31a2d99 --- /dev/null +++ b/Assets/FishNet/Upgrading/MirrorUpgrade.cs @@ -0,0 +1,428 @@ +#if UNITY_EDITOR +#if MIRROR +using UnityEditor; +using UnityEngine; +using FishNet.Object; +using FishNet.Documenting; +using System.Collections.Generic; +using FNNetworkTransform = FishNet.Component.Transforming.NetworkTransform; +using FNNetworkAnimator = FishNet.Component.Animating.NetworkAnimator; +using FNNetworkObserver = FishNet.Observing.NetworkObserver; +using FishNet.Observing; +using FishNet.Component.Observing; +using FishNet.Editing; +using System.IO; +using System.Collections; +using Mirror; +using MirrorNetworkTransformBase = Mirror.NetworkTransformBase; +using MirrorNetworkTransformChild = Mirror.NetworkTransformChild; +using MirrorNetworkAnimator = Mirror.NetworkAnimator; +#if !MIRROR_57_0_OR_NEWER +using MirrorNetworkProximityChecker = Mirror.NetworkProximityChecker; +using MirrorNetworkSceneChecker = Mirror.NetworkSceneChecker; +#endif + +#if FGG_ASSETS +using FlexNetworkAnimator = FirstGearGames.Mirrors.Assets.FlexNetworkAnimators.FlexNetworkAnimator; +using FlexNetworkTransformBase = FirstGearGames.Mirrors.Assets.FlexNetworkTransforms.FlexNetworkTransformBase; +using FastProximityChecker = FirstGearGames.Mirrors.Assets.NetworkProximities.FastProximityChecker; +#endif + +#if FGG_PROJECTS +using FlexSceneChecker = FirstGearGames.FlexSceneManager.FlexSceneChecker; +#endif + +namespace FishNet.Upgrading.Mirror.Editing +{ + + /* IMPORTANT IMPORTANT IMPORTANT IMPORTANT + * If you receive errors about missing Mirror components, + * such as NetworkIdentity, then remove MIRROR and any other + * MIRROR defines. + * Project Settings -> Player -> Other -> Scripting Define Symbols. + * + * If you are also using my assets add FGG_ASSETS to the defines, and + * then remove it after running this script. */ + [APIExclude] + [ExecuteInEditMode] + [InitializeOnLoad] + public class MirrorUpgrade : MonoBehaviour + { + /// + /// SceneCondition within FishNet. + /// + private SceneCondition _sceneCondition = null; + /// + /// DistanceCondition created for the user. + /// + private DistanceCondition _distanceCondition = null; + /// + /// + /// + private int _replacedNetworkTransforms; + /// + /// + /// + private int _replacedNetworkAnimators; + /// + /// + /// + private int _replacedNetworkIdentities; + /// + /// + /// + private int _replacedSceneCheckers; + /// + /// + /// + private int _replacedProximityCheckers; + /// + /// True if anything was changed. + /// + private bool _changed; + /// + /// Index in gameObjects to iterate. + /// + private int _goIndex = -1; + /// + /// Found gameObjects to iterate. + /// + private List _gameObjects = new List(); + /// + /// True if initialized. + /// + private bool _initialized; + + + private const string OBJECT_NAME_PREFIX = "MirrorUpgrade"; + + + private void Awake() + { + gameObject.name = OBJECT_NAME_PREFIX; + Debug.Log($"{gameObject.name} is working. Please wait until this object is removed from your hierarchy."); + EditorApplication.update += EditorUpdate; + } + + private void OnDestroy() + { + EditorApplication.update -= EditorUpdate; + } + + private void EditorUpdate() + { + if (!_initialized) + { + FindConditions(true); + _gameObjects = Finding.GetGameObjects(true, false, true, new string[] { "/Mirror/" }); + _goIndex = 0; + _initialized = true; + } + + if (_goIndex == -1) + return; + if (_goIndex >= _gameObjects.Count) + { + gameObject.name = $"{OBJECT_NAME_PREFIX} - 100%"; + Debug.Log($"Switched {_replacedNetworkTransforms} NetworkTransforms."); + Debug.Log($"Switched {_replacedNetworkAnimators} NetworkAnimators."); + Debug.Log($"Switched {_replacedSceneCheckers} SceneCheckers."); + Debug.Log($"Switched {_replacedProximityCheckers} ProximityCheckers."); + Debug.Log($"Switched {_replacedNetworkIdentities} NetworkIdentities."); + + if (_changed) + PrintSaveWarning(); + + DestroyImmediate(gameObject); + return; + } + + float percentFloat = ((float)_goIndex / (float)_gameObjects.Count) * 100f; + int percentInt = Mathf.FloorToInt(percentFloat); + gameObject.name = $"{OBJECT_NAME_PREFIX} - {percentInt}%"; + + GameObject go = _gameObjects[_goIndex]; + _goIndex++; + //Go went empty? + if (go == null) + return; + + /* When a component is removed + * changed is set true and remove count is increased. + * _goIndex is also returned before exiting the method. + * This will cause the same gameObject to iterate + * next update. This is important because the components + * must be Switched in order, and I can only remove one + * component per frame without Unity throwing a fit and + * freezing. A while loop doesn't let Unity recognize the component + * is gone(weird right? maybe editor thing), and a coroutine + * doesn't show errors well, they just fail silently. */ + + bool changedThisFrame = false; + if (IterateNetworkTransform(go)) + { + changedThisFrame = true; + _changed = true; + _replacedNetworkTransforms++; + } + if (IterateNetworkAnimator(go)) + { + changedThisFrame = true; + _changed = true; + _replacedNetworkAnimators++; + } + + if (IterateSceneChecker(go)) + { + changedThisFrame = true; + _changed = true; + _replacedSceneCheckers++; + } + if (IterateProximityChecker(go)) + { + changedThisFrame = true; + _changed = true; + _replacedProximityCheckers++; + } + if (changedThisFrame) + { + _goIndex--; + return; + } + //NetworkIdentity must be done last. + if (IterateNetworkIdentity(go)) + { + _changed = true; + _replacedNetworkIdentities++; + } + } + + + /// + /// Finds Condition scripts to be used with NetworkObserver. + /// + /// + private void FindConditions(bool error) + { + List scriptableObjects; + + if (_sceneCondition == null) + { + scriptableObjects = Finding.GetScriptableObjects(true, true); + //Use the first found scene condition, there should be only one. + if (scriptableObjects.Count > 0) + _sceneCondition = (SceneCondition)scriptableObjects[0]; + + if (_sceneCondition == null && error) + Debug.LogError("SceneCondition could not be found. Upgrading scene checker components will not function."); + } + + if (_distanceCondition == null) + { + scriptableObjects = Finding.GetScriptableObjects(false, true); + if (scriptableObjects.Count > 0) + { + _distanceCondition = (DistanceCondition)scriptableObjects[0]; + } + else + { + DistanceCondition dc = ScriptableObject.CreateInstance(); + string savePath = "Assets"; + AssetDatabase.CreateAsset(dc, Path.Combine(savePath, $"CreatedDistanceCondition.asset")); + Debug.LogWarning($"DistanceCondition has been created at {savePath}. Place this file somewhere within your project and change settings to your liking."); + } + + if (_distanceCondition == null && error) + Debug.LogError("DistanceCondition could not be found. Upgrading proximity checker components will not function."); + } + } + + + private bool IterateNetworkTransform(GameObject go) + { + if (go.TryGetComponent(out MirrorNetworkTransformBase nt1)) + { + Transform target; + if (nt1 is MirrorNetworkTransformChild mc1) + target = mc1.target; + else + target = go.transform; + Replace(nt1, target); + return true; + } +#if FGG_ASSETS + if (go.TryGetComponent(out FlexNetworkTransformBase fntb)) + { + Replace(fntb, fntb.TargetTransform); + return true; + } +#endif + + void Replace(UnityEngine.Component component, Transform target) + { + EditorUtility.SetDirty(go); + DestroyImmediate(component, true); + + if (target != null && !target.TryGetComponent(out _)) + target.gameObject.AddComponent(); + } + + //Fall through, nothing was replaced. + return false; + } + + private bool IterateNetworkAnimator(GameObject go) + { + if (go.TryGetComponent(out MirrorNetworkAnimator mna)) + { + Replace(mna, mna.transform); + return true; + } +#if FGG_ASSETS + if (go.TryGetComponent(out FlexNetworkAnimator fna)) + { + Replace(fna, fna.transform); + return true; + } +#endif + + void Replace(UnityEngine.Component component, Transform target) + { + EditorUtility.SetDirty(go); + DestroyImmediate(component, true); + + if (target == null) + return; + if (!target.TryGetComponent(out _)) + target.gameObject.AddComponent(); + } + + return false; + } + + + private bool IterateSceneChecker(GameObject go) + { +#if !MIRROR_57_0_OR_NEWER + if (_sceneCondition == null) + return false; + + if (go.TryGetComponent(out MirrorNetworkSceneChecker msc)) + { + Replace(msc); + return true; + } +#if FGG_PROJECTS + if (go.TryGetComponent(out FlexSceneChecker fsc)) + { + Replace(fsc); + return true; + } +#endif + + void Replace(UnityEngine.Component component) + { + EditorUtility.SetDirty(go); + DestroyImmediate(component, true); + + FNNetworkObserver networkObserver; + if (!go.TryGetComponent(out networkObserver)) + networkObserver = go.AddComponent(); + + bool conditionFound = false; + foreach (ObserverCondition condition in networkObserver.ObserverConditions) + { + if (condition.GetType() == typeof(SceneCondition)) + { + conditionFound = true; + break; + } + } + + //If not able to find scene condition then add one. + if (!conditionFound) + networkObserver.ObserverConditionsInternal.Add(_sceneCondition); + } + +#endif + return false; + } + + + + private bool IterateProximityChecker(GameObject go) + { +#if !MIRROR_57_0_OR_NEWER + if (_distanceCondition == null) + return false; + + if (go.TryGetComponent(out MirrorNetworkProximityChecker mnpc)) + { + Replace(mnpc); + return true; + } +#if FGG_PROJECTS + if (go.TryGetComponent(out FastProximityChecker fpc)) + { + Replace(fpc); + return true; + } +#endif + + void Replace(UnityEngine.Component component) + { + EditorUtility.SetDirty(go); + DestroyImmediate(component, true); + + FNNetworkObserver networkObserver; + if (!go.TryGetComponent(out networkObserver)) + networkObserver = go.AddComponent(); + + bool conditionFound = false; + foreach (ObserverCondition condition in networkObserver.ObserverConditions) + { + if (condition.GetType() == typeof(DistanceCondition)) + { + conditionFound = true; + break; + } + } + + //If not able to find scene condition then add one. + if (!conditionFound) + networkObserver.ObserverConditionsInternal.Add(_distanceCondition); + } +#endif + + return false; + } + + + private bool IterateNetworkIdentity(GameObject go) + { + if (go.TryGetComponent(out NetworkIdentity netIdentity)) + { + EditorUtility.SetDirty(go); + DestroyImmediate(netIdentity, true); + + //Add nob if doesn't exist. + if (!go.TryGetComponent(out _)) + go.AddComponent(); + + return true; + } + + return false; + } + + + private static void PrintSaveWarning() + { + Debug.LogWarning("You must File -> Save for changes to complete."); + } + } + + +} +#endif +#endif \ No newline at end of file diff --git a/Assets/FishNet/Upgrading/MirrorUpgrade.cs.meta b/Assets/FishNet/Upgrading/MirrorUpgrade.cs.meta new file mode 100644 index 0000000..b1d33a8 --- /dev/null +++ b/Assets/FishNet/Upgrading/MirrorUpgrade.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 491b9891492df1444937419bc0e39642 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Upgrading/UpgradeFromMirrorMenu.cs b/Assets/FishNet/Upgrading/UpgradeFromMirrorMenu.cs new file mode 100644 index 0000000..5d77c2b --- /dev/null +++ b/Assets/FishNet/Upgrading/UpgradeFromMirrorMenu.cs @@ -0,0 +1,75 @@ +#if UNITY_EDITOR +using FishNet.Documenting; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace FishNet.Upgrading.Mirror.Editing +{ + + /* IMPORTANT IMPORTANT IMPORTANT IMPORTANT + * If you receive errors about missing Mirror components, + * such as NetworkIdentity, then remove MIRROR and any other + * MIRROR defines. + * Project Settings -> Player -> Other -> Scripting Define Symbols. + * + * If you are also using my assets add FGG_ASSETS to the defines, and + * then remove it after running this script. */ + [APIExclude] + public class UpgradeFromMirrorMenu : MonoBehaviour + { + + /// + /// Replaces all components. + /// + [MenuItem("Fish-Networking/Upgrading/From Mirror/Replace Components", false,2)] + private static void ReplaceComponents() + { +#if MIRROR + MirrorUpgrade result = GameObject.FindObjectOfType(); + if (result != null) + { + Debug.LogError("MirrorUpgrade already exist in the scene. This suggests an operation is currently running."); + return; + } + + GameObject iteratorGo = new GameObject(); + iteratorGo.AddComponent(); +#else + Debug.LogError("Mirror must be imported to perform this function."); +#endif + } + + [MenuItem("Fish-Networking/Upgrading/From Mirror/Remove Defines", false, 2)] + private static void RemoveDefines() + { + string currentDefines = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup); + /* Convert current defines into a hashset. This is so we can + * determine if any of our defines were added. Only save playersettings + * when a define is added. */ + HashSet definesHs = new HashSet(); + string[] currentArr = currentDefines.Split(';'); + + bool removed = false; + //Add any define which doesn't contain MIRROR. + foreach (string item in currentArr) + { + string itemLower = item.ToLower(); + if (itemLower != "mirror" && !itemLower.StartsWith("mirror_")) + definesHs.Add(item); + else + removed = true; + } + + if (removed) + { + Debug.Log("Removed Mirror defines to player settings."); + string changedDefines = string.Join(";", definesHs); + PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, changedDefines); + } + } + + + } +} +#endif diff --git a/Assets/FishNet/Upgrading/UpgradeFromMirrorMenu.cs.meta b/Assets/FishNet/Upgrading/UpgradeFromMirrorMenu.cs.meta new file mode 100644 index 0000000..726fd8d --- /dev/null +++ b/Assets/FishNet/Upgrading/UpgradeFromMirrorMenu.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 55cd1de3399bf564a9545f089421a88d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/VERSION.txt b/Assets/FishNet/VERSION.txt new file mode 100644 index 0000000..a37a75c --- /dev/null +++ b/Assets/FishNet/VERSION.txt @@ -0,0 +1 @@ +3.5.8 \ No newline at end of file diff --git a/Assets/FishNet/VERSION.txt.meta b/Assets/FishNet/VERSION.txt.meta new file mode 100644 index 0000000..fbab156 --- /dev/null +++ b/Assets/FishNet/VERSION.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 84a91b39bff26504e9cf03f8aa4fe3ab +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset index 522cc1d..e33c5ee 100644 --- a/ProjectSettings/ProjectSettings.asset +++ b/ProjectSettings/ProjectSettings.asset @@ -631,7 +631,8 @@ PlayerSettings: webGLThreadsSupport: 0 webGLDecompressionFallback: 0 webGLPowerPreference: 2 - scriptingDefineSymbols: {} + scriptingDefineSymbols: + Standalone: FISHNET;FISHNET_V3 additionalCompilerArguments: {} platformArchitecture: {} scriptingBackend: {}